Skip to content

Commit 41a8e81

Browse files
drxddyCommit Queue
authored and
Commit Queue
committed
[CoreLib] Iterable.withIterator
Closes #59908 GitOrigin-RevId: bf05a54 Change-Id: Ie511e79c7cbc1e9230477913a54191730104f489 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/404341 Reviewed-by: Jake Macdonald <[email protected]> Commit-Queue: Lasse Nielsen <[email protected]>
1 parent 57d877f commit 41a8e81

File tree

3 files changed

+133
-0
lines changed

3 files changed

+133
-0
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
## 3.8.0
22

3+
### Libraries
4+
5+
#### `dart:core`
6+
7+
- Added `Iterable.withIterator` constructor.
8+
39
## 3.7.0
410

511
### Language

sdk/lib/core/iterable.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,24 @@ abstract mixin class Iterable<E> {
127127
return _GeneratorIterable<E>(count, generator);
128128
}
129129

130+
/// Creates an [Iterable] from the [Iterator] factory.
131+
///
132+
/// The returned iterable creates a new iterator each time [iterator] is read,
133+
/// by calling the provided [iteratorFactory] function. The [iteratorFactory]
134+
/// function must return a new instance of `Iterator<E>` on each call.
135+
///
136+
/// This factory is useful when you need to create an iterable from a custom
137+
/// iterator, or when you want to ensure a fresh iteration state on each use.
138+
///
139+
/// Example:
140+
/// ```dart
141+
/// final numbers = Iterable.withIterator(() => [1, 2, 3].iterator);
142+
/// print(numbers.toList()); // [1, 2, 3]
143+
/// ```
144+
@Since("3.8")
145+
factory Iterable.withIterator(Iterator<E> Function() iteratorFactory) =
146+
_WithIteratorIterable<E>;
147+
130148
/// Creates an empty iterable.
131149
///
132150
/// The empty iterable has no elements, and iterating it always stops
@@ -915,6 +933,20 @@ class _GeneratorIterable<E> extends ListIterable<E> {
915933
static int _id(int n) => n;
916934
}
917935

936+
/// Used internally by [Iterable.withIterator].
937+
///
938+
/// This class implements [Iterable] by delegating the iterator creation
939+
/// to the provided function.
940+
class _WithIteratorIterable<E> extends Iterable<E> {
941+
final Iterator<E> Function() _iteratorFactory;
942+
943+
/// Creates an iterable that gets its elements from iterators created by
944+
/// the provided factory function.
945+
_WithIteratorIterable(this._iteratorFactory);
946+
947+
Iterator<E> get iterator => _iteratorFactory();
948+
}
949+
918950
/// Convert elements of [iterable] to strings and store them in [parts].
919951
void _iterablePartsToStrings(Iterable<Object?> iterable, List<String> parts) {
920952
// This is the complicated part of [iterableToShortString].
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import "package:expect/expect.dart";
2+
3+
void main() {
4+
testIteratorFactory();
5+
testIteratorEquality();
6+
}
7+
8+
void testIteratorFactory() {
9+
Iterator<Object?>? returnedIterator;
10+
Object? thrownError;
11+
int calls = 0;
12+
Iterator<T> f<T>() {
13+
calls++;
14+
if (thrownError case var error?) throw error;
15+
return returnedIterator as Iterator<T>;
16+
}
17+
18+
var ints = Iterable.withIterator(f<int>);
19+
var intIterator = <int>[].iterator;
20+
returnedIterator = intIterator;
21+
Expect.identical(intIterator, ints.iterator, "First iterator should match");
22+
Expect.equals(1, calls, "First call count");
23+
Expect.identical(intIterator, ints.iterator, "Second iterator should match");
24+
Expect.equals(2, calls, "Second call count");
25+
26+
var intIterator2 = <int>[].iterator;
27+
Expect.notIdentical(
28+
intIterator,
29+
intIterator2,
30+
"Different iterators should not be identical",
31+
);
32+
returnedIterator = intIterator2;
33+
Expect.identical(intIterator2, ints.iterator, "Third iterator should match");
34+
Expect.equals(3, calls, "Third call count");
35+
Expect.identical(intIterator2, ints.iterator, "Fourth iterator should match");
36+
Expect.equals(4, calls, "Fourth call count");
37+
38+
var error = StateError("quo");
39+
thrownError = error;
40+
Expect.identical(
41+
error,
42+
Expect.throws(() => ints.iterator),
43+
"Should throw first error",
44+
);
45+
Expect.equals(5, calls, "Fifth call count");
46+
Expect.identical(
47+
error,
48+
Expect.throws(() => ints.iterator),
49+
"Should throw second error",
50+
);
51+
Expect.equals(6, calls, "Sixth call count");
52+
53+
thrownError = null;
54+
Expect.identical(intIterator2, ints.iterator, "Fifth iterator should match");
55+
Expect.equals(7, calls, "Seventh call count");
56+
57+
var objectqs = Iterable.withIterator(f<Object?>);
58+
Expect.identical(
59+
intIterator2,
60+
objectqs.iterator,
61+
"Object iterator should match",
62+
);
63+
Expect.equals(8, calls, "Eighth call count");
64+
65+
var nulls = Iterable.withIterator(f<Null>);
66+
var nullIterator = <Null>[].iterator;
67+
returnedIterator = nullIterator;
68+
Expect.identical(nullIterator, nulls.iterator, "Null iterator should match");
69+
Expect.equals(9, calls, "Ninth call count");
70+
71+
var nevers = Iterable.withIterator(f<Never>);
72+
thrownError = error;
73+
Expect.identical(
74+
error,
75+
Expect.throws(() => nevers.iterator),
76+
"Should throw third error",
77+
);
78+
Expect.equals(10, calls, "Tenth call count");
79+
Expect.identical(
80+
error,
81+
Expect.throws(() => nevers.iterator),
82+
"Should throw fourth error",
83+
);
84+
Expect.equals(11, calls, "Eleventh call count");
85+
}
86+
87+
void testIteratorEquality() {
88+
var iterable1 = Iterable.withIterator(() => [1, 2, 3].iterator);
89+
var iterable2 = Iterable.withIterator(() => [1, 2, 3].iterator);
90+
Expect.listEquals(
91+
iterable1.toList(),
92+
iterable2.toList(),
93+
"Iterator contents should be equal",
94+
);
95+
}

0 commit comments

Comments
 (0)