[CoreLib] Iterable.withIterator

Closes https://github.com/dart-lang/sdk/pull/59908

GitOrigin-RevId: bf05a5468020756e5a16b34134c9ec472b95576f
Change-Id: Ie511e79c7cbc1e9230477913a54191730104f489
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/404341
Reviewed-by: Jake Macdonald <jakemac@google.com>
Commit-Queue: Lasse Nielsen <lrn@google.com>
This commit is contained in:
Dhikshith Reddy
2025-01-21 05:36:47 -08:00
committed by Commit Queue
parent 57d877ff2e
commit 41a8e8147f
3 changed files with 133 additions and 0 deletions
+6
View File
@@ -1,5 +1,11 @@
## 3.8.0
### Libraries
#### `dart:core`
- Added `Iterable.withIterator` constructor.
## 3.7.0
### Language
+32
View File
@@ -127,6 +127,24 @@ abstract mixin class Iterable<E> {
return _GeneratorIterable<E>(count, generator);
}
/// Creates an [Iterable] from the [Iterator] factory.
///
/// The returned iterable creates a new iterator each time [iterator] is read,
/// by calling the provided [iteratorFactory] function. The [iteratorFactory]
/// function must return a new instance of `Iterator<E>` on each call.
///
/// This factory is useful when you need to create an iterable from a custom
/// iterator, or when you want to ensure a fresh iteration state on each use.
///
/// Example:
/// ```dart
/// final numbers = Iterable.withIterator(() => [1, 2, 3].iterator);
/// print(numbers.toList()); // [1, 2, 3]
/// ```
@Since("3.8")
factory Iterable.withIterator(Iterator<E> Function() iteratorFactory) =
_WithIteratorIterable<E>;
/// Creates an empty iterable.
///
/// The empty iterable has no elements, and iterating it always stops
@@ -915,6 +933,20 @@ class _GeneratorIterable<E> extends ListIterable<E> {
static int _id(int n) => n;
}
/// Used internally by [Iterable.withIterator].
///
/// This class implements [Iterable] by delegating the iterator creation
/// to the provided function.
class _WithIteratorIterable<E> extends Iterable<E> {
final Iterator<E> Function() _iteratorFactory;
/// Creates an iterable that gets its elements from iterators created by
/// the provided factory function.
_WithIteratorIterable(this._iteratorFactory);
Iterator<E> get iterator => _iteratorFactory();
}
/// Convert elements of [iterable] to strings and store them in [parts].
void _iterablePartsToStrings(Iterable<Object?> iterable, List<String> parts) {
// This is the complicated part of [iterableToShortString].
@@ -0,0 +1,95 @@
import "package:expect/expect.dart";
void main() {
testIteratorFactory();
testIteratorEquality();
}
void testIteratorFactory() {
Iterator<Object?>? returnedIterator;
Object? thrownError;
int calls = 0;
Iterator<T> f<T>() {
calls++;
if (thrownError case var error?) throw error;
return returnedIterator as Iterator<T>;
}
var ints = Iterable.withIterator(f<int>);
var intIterator = <int>[].iterator;
returnedIterator = intIterator;
Expect.identical(intIterator, ints.iterator, "First iterator should match");
Expect.equals(1, calls, "First call count");
Expect.identical(intIterator, ints.iterator, "Second iterator should match");
Expect.equals(2, calls, "Second call count");
var intIterator2 = <int>[].iterator;
Expect.notIdentical(
intIterator,
intIterator2,
"Different iterators should not be identical",
);
returnedIterator = intIterator2;
Expect.identical(intIterator2, ints.iterator, "Third iterator should match");
Expect.equals(3, calls, "Third call count");
Expect.identical(intIterator2, ints.iterator, "Fourth iterator should match");
Expect.equals(4, calls, "Fourth call count");
var error = StateError("quo");
thrownError = error;
Expect.identical(
error,
Expect.throws(() => ints.iterator),
"Should throw first error",
);
Expect.equals(5, calls, "Fifth call count");
Expect.identical(
error,
Expect.throws(() => ints.iterator),
"Should throw second error",
);
Expect.equals(6, calls, "Sixth call count");
thrownError = null;
Expect.identical(intIterator2, ints.iterator, "Fifth iterator should match");
Expect.equals(7, calls, "Seventh call count");
var objectqs = Iterable.withIterator(f<Object?>);
Expect.identical(
intIterator2,
objectqs.iterator,
"Object iterator should match",
);
Expect.equals(8, calls, "Eighth call count");
var nulls = Iterable.withIterator(f<Null>);
var nullIterator = <Null>[].iterator;
returnedIterator = nullIterator;
Expect.identical(nullIterator, nulls.iterator, "Null iterator should match");
Expect.equals(9, calls, "Ninth call count");
var nevers = Iterable.withIterator(f<Never>);
thrownError = error;
Expect.identical(
error,
Expect.throws(() => nevers.iterator),
"Should throw third error",
);
Expect.equals(10, calls, "Tenth call count");
Expect.identical(
error,
Expect.throws(() => nevers.iterator),
"Should throw fourth error",
);
Expect.equals(11, calls, "Eleventh call count");
}
void testIteratorEquality() {
var iterable1 = Iterable.withIterator(() => [1, 2, 3].iterator);
var iterable2 = Iterable.withIterator(() => [1, 2, 3].iterator);
Expect.listEquals(
iterable1.toList(),
iterable2.toList(),
"Iterator contents should be equal",
);
}