Skip to content

Commit cd3e866

Browse files
Rollup merge of #153880 - asder8215:intersperse_nonfused, r=Mark-Simulacrum
Lifted intersperse and intersperse_with Fused transformation and updated documentation + tests This PR once again builds on top of #152855. From the discussion in #152855, libs team came to the conclusion that `intersperse`/`intersperse_with` shouldn't transform the given iterator to a fused iterator always and a separator should be emitted between `Some(_)` items for non fused iterators (particularly just right before the subsequent `Some(_)`). On top of the change Zakarumych added in the PR, I lifted the `FusedIterator` trait and transformation of the inner `iter` for `Intersperse`/`IntersperseWith`, so that we can display this behavior. I've adjusted the documentation and tests accordingly to reflect this change as well. r? @jhpratt
2 parents 6e6e266 + 800d9ea commit cd3e866

3 files changed

Lines changed: 101 additions & 104 deletions

File tree

library/core/src/iter/adapters/intersperse.rs

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::fmt;
2-
use crate::iter::{Fuse, FusedIterator};
32

43
/// An iterator adapter that places a separator between all elements.
54
///
@@ -14,23 +13,15 @@ where
1413
started: bool,
1514
separator: I::Item,
1615
next_item: Option<I::Item>,
17-
iter: Fuse<I>,
18-
}
19-
20-
#[unstable(feature = "iter_intersperse", issue = "79524")]
21-
impl<I> FusedIterator for Intersperse<I>
22-
where
23-
I: FusedIterator,
24-
I::Item: Clone,
25-
{
16+
iter: I,
2617
}
2718

2819
impl<I: Iterator> Intersperse<I>
2920
where
3021
I::Item: Clone,
3122
{
3223
pub(in crate::iter) fn new(iter: I, separator: I::Item) -> Self {
33-
Self { started: false, separator, next_item: None, iter: iter.fuse() }
24+
Self { started: false, separator, next_item: None, iter }
3425
}
3526
}
3627

@@ -57,8 +48,9 @@ where
5748
}
5849
}
5950
} else {
60-
self.started = true;
61-
self.iter.next()
51+
let item = self.iter.next();
52+
self.started = item.is_some();
53+
item
6254
}
6355
}
6456

@@ -95,15 +87,7 @@ where
9587
started: bool,
9688
separator: G,
9789
next_item: Option<I::Item>,
98-
iter: Fuse<I>,
99-
}
100-
101-
#[unstable(feature = "iter_intersperse", issue = "79524")]
102-
impl<I, G> FusedIterator for IntersperseWith<I, G>
103-
where
104-
I: FusedIterator,
105-
G: FnMut() -> I::Item,
106-
{
90+
iter: I,
10791
}
10892

10993
#[unstable(feature = "iter_intersperse", issue = "79524")]
@@ -146,7 +130,7 @@ where
146130
G: FnMut() -> I::Item,
147131
{
148132
pub(in crate::iter) fn new(iter: I, separator: G) -> Self {
149-
Self { started: false, separator, next_item: None, iter: iter.fuse() }
133+
Self { started: false, separator, next_item: None, iter }
150134
}
151135
}
152136

@@ -173,8 +157,9 @@ where
173157
}
174158
}
175159
} else {
176-
self.started = true;
177-
self.iter.next()
160+
let item = self.iter.next();
161+
self.started = item.is_some();
162+
item
178163
}
179164
}
180165

library/core/src/iter/traits/iterator.rs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -635,11 +635,24 @@ pub const trait Iterator {
635635
/// of the original iterator.
636636
///
637637
/// Specifically on fused iterators, it is guaranteed that the new iterator
638-
/// places a copy of `separator` between adjacent `Some(_)` items. However,
639-
/// for non-fused iterators, [`intersperse`] will create a new iterator that
640-
/// is a fused version of the original iterator and place a copy of `separator`
641-
/// between adjacent `Some(_)` items. This behavior for non-fused iterators
642-
/// is subject to change.
638+
/// places a copy of `separator` between *adjacent* `Some(_)` items. For non-fused iterators,
639+
/// it is guaranteed that [`intersperse`] will create a new iterator that places a copy
640+
/// of `separator` between `Some(_)` items, particularly just right before the subsequent
641+
/// `Some(_)` item.
642+
///
643+
/// For example, consider the following non-fused iterator:
644+
///
645+
/// ```text
646+
/// Some(1) -> Some(2) -> None -> Some(3) -> Some(4) -> ...
647+
/// ```
648+
///
649+
/// If this non-fused iterator were to be interspersed with `0`,
650+
/// then the interspersed iterator will produce:
651+
///
652+
/// ```text
653+
/// Some(1) -> Some(0) -> Some(2) -> None -> Some(0) -> Some(3) -> Some(0) ->
654+
/// Some(4) -> ...
655+
/// ```
643656
///
644657
/// In case `separator` does not implement [`Clone`] or needs to be
645658
/// computed every time, use [`intersperse_with`].
@@ -688,10 +701,23 @@ pub const trait Iterator {
688701
///
689702
/// Specifically on fused iterators, it is guaranteed that the new iterator
690703
/// places an item generated by `separator` between adjacent `Some(_)` items.
691-
/// However, for non-fused iterators, [`intersperse_with`] will create a new
692-
/// iterator that is a fused version of the original iterator and place an item
693-
/// generated by `separator` between adjacent `Some(_)` items. This
694-
/// behavior for non-fused iterators is subject to change.
704+
/// For non-fused iterators, it is guaranteed that [`intersperse_with`] will
705+
/// create a new iterator that places an item generated by `separator` between `Some(_)`
706+
/// items, particularly just right before the subsequent `Some(_)` item.
707+
///
708+
/// For example, consider the following non-fused iterator:
709+
///
710+
/// ```text
711+
/// Some(1) -> Some(2) -> None -> Some(3) -> Some(4) -> ...
712+
/// ```
713+
///
714+
/// If this non-fused iterator were to be interspersed with a `separator` closure
715+
/// that returns `0` repeatedly, the interspersed iterator will produce:
716+
///
717+
/// ```text
718+
/// Some(1) -> Some(0) -> Some(2) -> None -> Some(0) -> Some(3) -> Some(0) ->
719+
/// Some(4) -> ...
720+
/// ```
695721
///
696722
/// The `separator` closure will be called exactly once each time an item
697723
/// is placed between two adjacent items from the underlying iterator;

library/coretests/tests/iter/adapters/intersperse.rs

Lines changed: 56 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,6 @@ fn test_try_fold_specialization_intersperse_err() {
153153
assert_eq!(iter.next(), None);
154154
}
155155

156-
// FIXME(iter_intersperse): `intersperse` current behavior may change for
157-
// non-fused iterators, so this test will likely have to
158-
// be adjusted; see PR #152855 and issue #79524
159-
// if `intersperse` doesn't change, remove this FIXME.
160156
#[test]
161157
fn test_non_fused_iterator_intersperse() {
162158
#[derive(Debug)]
@@ -183,24 +179,26 @@ fn test_non_fused_iterator_intersperse() {
183179
}
184180

185181
let counter = 0;
186-
// places a 2 between `Some(_)` items
182+
// places a 1 between `Some(_)` items
187183
let non_fused_iter = TestCounter { counter };
188-
let mut intersperse_iter = non_fused_iter.intersperse(2);
189-
// Since `intersperse` currently transforms the original
190-
// iterator into a fused iterator, this intersperse_iter
191-
// should always have `None`
192-
for _ in 0..counter + 6 {
193-
assert_eq!(intersperse_iter.next(), None);
194-
}
184+
let mut intersperse_iter = non_fused_iter.intersperse(1);
185+
// Interspersed iter produces:
186+
// `None` -> `Some(2)` -> `None` -> `Some(1)` -> Some(4)` -> `None` -> `Some(1)` ->
187+
// `Some(6)` -> and then `None` endlessly
188+
assert_eq!(intersperse_iter.next(), None);
189+
assert_eq!(intersperse_iter.next(), Some(2));
190+
assert_eq!(intersperse_iter.next(), None);
191+
assert_eq!(intersperse_iter.next(), Some(1));
192+
assert_eq!(intersperse_iter.next(), Some(4));
193+
assert_eq!(intersperse_iter.next(), None);
194+
assert_eq!(intersperse_iter.next(), Some(1));
195+
assert_eq!(intersperse_iter.next(), Some(6));
196+
assert_eq!(intersperse_iter.next(), None);
195197

196-
// Extra check to make sure it is `None` after processing 6 items
198+
// Extra check to make sure it is `None` after processing all items
197199
assert_eq!(intersperse_iter.next(), None);
198200
}
199201

200-
// FIXME(iter_intersperse): `intersperse` current behavior may change for
201-
// non-fused iterators, so this test will likely have to
202-
// be adjusted; see PR #152855 and issue #79524
203-
// if `intersperse` doesn't change, remove this FIXME.
204202
#[test]
205203
fn test_non_fused_iterator_intersperse_2() {
206204
#[derive(Debug)]
@@ -228,35 +226,26 @@ fn test_non_fused_iterator_intersperse_2() {
228226
}
229227

230228
let counter = 0;
231-
// places a 2 between `Some(_)` items
229+
// places a 100 between `Some(_)` items
232230
let non_fused_iter = TestCounter { counter };
233-
let mut intersperse_iter = non_fused_iter.intersperse(2);
234-
// Since `intersperse` currently transforms the original
235-
// iterator into a fused iterator, this interspersed iter
236-
// will be `Some(1)` -> `Some(2)` -> `Some(2)` -> and then
237-
// `None` endlessly
238-
let mut items_processed = 0;
239-
for num in 0..counter + 6 {
240-
if num < 3 {
241-
if num % 2 != 0 {
242-
assert_eq!(intersperse_iter.next(), Some(2));
243-
} else {
244-
items_processed += 1;
245-
assert_eq!(intersperse_iter.next(), Some(items_processed));
246-
}
247-
} else {
248-
assert_eq!(intersperse_iter.next(), None);
249-
}
250-
}
231+
let mut intersperse_iter = non_fused_iter.intersperse(100);
232+
// Interspersed iter produces:
233+
// `Some(1)` -> `Some(100)` -> `Some(2)` -> `None` -> `Some(100)`
234+
// -> `Some(4)` -> `Some(100)` -> `Some(5)` -> `None` endlessly
235+
assert_eq!(intersperse_iter.next(), Some(1));
236+
assert_eq!(intersperse_iter.next(), Some(100));
237+
assert_eq!(intersperse_iter.next(), Some(2));
238+
assert_eq!(intersperse_iter.next(), None);
239+
assert_eq!(intersperse_iter.next(), Some(100));
240+
assert_eq!(intersperse_iter.next(), Some(4));
241+
assert_eq!(intersperse_iter.next(), Some(100));
242+
assert_eq!(intersperse_iter.next(), Some(5));
243+
assert_eq!(intersperse_iter.next(), None);
251244

252245
// Extra check to make sure it is `None` after processing 6 items
253246
assert_eq!(intersperse_iter.next(), None);
254247
}
255248

256-
// FIXME(iter_intersperse): `intersperse_with` current behavior may change for
257-
// non-fused iterators, so this test will likely have to
258-
// be adjusted; see PR #152855 and issue #79524
259-
// if `intersperse_with` doesn't change, remove this FIXME.
260249
#[test]
261250
fn test_non_fused_iterator_intersperse_with() {
262251
#[derive(Debug)]
@@ -285,22 +274,24 @@ fn test_non_fused_iterator_intersperse_with() {
285274
let counter = 0;
286275
let non_fused_iter = TestCounter { counter };
287276
// places a 2 between `Some(_)` items
288-
let mut intersperse_iter = non_fused_iter.intersperse_with(|| 2);
289-
// Since `intersperse` currently transforms the original
290-
// iterator into a fused iterator, this intersperse_iter
291-
// should always have `None`
292-
for _ in 0..counter + 6 {
293-
assert_eq!(intersperse_iter.next(), None);
294-
}
277+
let mut intersperse_iter = non_fused_iter.intersperse_with(|| 1);
278+
// Interspersed iter produces:
279+
// `None` -> `Some(2)` -> `None` -> `Some(1)` -> Some(4)` -> `None` -> `Some(1)` ->
280+
// `Some(6)` -> and then `None` endlessly
281+
assert_eq!(intersperse_iter.next(), None);
282+
assert_eq!(intersperse_iter.next(), Some(2));
283+
assert_eq!(intersperse_iter.next(), None);
284+
assert_eq!(intersperse_iter.next(), Some(1));
285+
assert_eq!(intersperse_iter.next(), Some(4));
286+
assert_eq!(intersperse_iter.next(), None);
287+
assert_eq!(intersperse_iter.next(), Some(1));
288+
assert_eq!(intersperse_iter.next(), Some(6));
289+
assert_eq!(intersperse_iter.next(), None);
295290

296291
// Extra check to make sure it is `None` after processing 6 items
297292
assert_eq!(intersperse_iter.next(), None);
298293
}
299294

300-
// FIXME(iter_intersperse): `intersperse_with` current behavior may change for
301-
// non-fused iterators, so this test will likely have to
302-
// be adjusted; see PR #152855 and issue #79524
303-
// if `intersperse_with` doesn't change, remove this FIXME.
304295
#[test]
305296
fn test_non_fused_iterator_intersperse_with_2() {
306297
#[derive(Debug)]
@@ -328,26 +319,21 @@ fn test_non_fused_iterator_intersperse_with_2() {
328319
}
329320

330321
let counter = 0;
331-
// places a 2 between `Some(_)` items
322+
// places a 100 between `Some(_)` items
332323
let non_fused_iter = TestCounter { counter };
333-
let mut intersperse_iter = non_fused_iter.intersperse(2);
334-
// Since `intersperse` currently transforms the original
335-
// iterator into a fused iterator, this interspersed iter
336-
// will be `Some(1)` -> `Some(2)` -> `Some(2)` -> and then
337-
// `None` endlessly
338-
let mut items_processed = 0;
339-
for num in 0..counter + 6 {
340-
if num < 3 {
341-
if num % 2 != 0 {
342-
assert_eq!(intersperse_iter.next(), Some(2));
343-
} else {
344-
items_processed += 1;
345-
assert_eq!(intersperse_iter.next(), Some(items_processed));
346-
}
347-
} else {
348-
assert_eq!(intersperse_iter.next(), None);
349-
}
350-
}
324+
let mut intersperse_iter = non_fused_iter.intersperse_with(|| 100);
325+
// Interspersed iter produces:
326+
// `Some(1)` -> `Some(100)` -> `Some(2)` -> `None` -> `Some(100)`
327+
// -> `Some(4)` -> `Some(100)` -> `Some(5)` -> `None` endlessly
328+
assert_eq!(intersperse_iter.next(), Some(1));
329+
assert_eq!(intersperse_iter.next(), Some(100));
330+
assert_eq!(intersperse_iter.next(), Some(2));
331+
assert_eq!(intersperse_iter.next(), None);
332+
assert_eq!(intersperse_iter.next(), Some(100));
333+
assert_eq!(intersperse_iter.next(), Some(4));
334+
assert_eq!(intersperse_iter.next(), Some(100));
335+
assert_eq!(intersperse_iter.next(), Some(5));
336+
assert_eq!(intersperse_iter.next(), None);
351337

352338
// Extra check to make sure it is `None` after processing 6 items
353339
assert_eq!(intersperse_iter.next(), None);

0 commit comments

Comments
 (0)