mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
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 rust-lang/rust#152855. From the discussion in rust-lang/rust#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
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
use crate::fmt;
|
||||
use crate::iter::{Fuse, FusedIterator};
|
||||
|
||||
/// An iterator adapter that places a separator between all elements.
|
||||
///
|
||||
@@ -14,15 +13,7 @@ pub struct Intersperse<I: Iterator>
|
||||
started: bool,
|
||||
separator: I::Item,
|
||||
next_item: Option<I::Item>,
|
||||
iter: Fuse<I>,
|
||||
}
|
||||
|
||||
#[unstable(feature = "iter_intersperse", issue = "79524")]
|
||||
impl<I> FusedIterator for Intersperse<I>
|
||||
where
|
||||
I: FusedIterator,
|
||||
I::Item: Clone,
|
||||
{
|
||||
iter: I,
|
||||
}
|
||||
|
||||
impl<I: Iterator> Intersperse<I>
|
||||
@@ -30,7 +21,7 @@ impl<I: Iterator> Intersperse<I>
|
||||
I::Item: Clone,
|
||||
{
|
||||
pub(in crate::iter) fn new(iter: I, separator: I::Item) -> Self {
|
||||
Self { started: false, separator, next_item: None, iter: iter.fuse() }
|
||||
Self { started: false, separator, next_item: None, iter }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,8 +48,9 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.started = true;
|
||||
self.iter.next()
|
||||
let item = self.iter.next();
|
||||
self.started = item.is_some();
|
||||
item
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,15 +87,7 @@ pub struct IntersperseWith<I, G>
|
||||
started: bool,
|
||||
separator: G,
|
||||
next_item: Option<I::Item>,
|
||||
iter: Fuse<I>,
|
||||
}
|
||||
|
||||
#[unstable(feature = "iter_intersperse", issue = "79524")]
|
||||
impl<I, G> FusedIterator for IntersperseWith<I, G>
|
||||
where
|
||||
I: FusedIterator,
|
||||
G: FnMut() -> I::Item,
|
||||
{
|
||||
iter: I,
|
||||
}
|
||||
|
||||
#[unstable(feature = "iter_intersperse", issue = "79524")]
|
||||
@@ -146,7 +130,7 @@ impl<I, G> IntersperseWith<I, G>
|
||||
G: FnMut() -> I::Item,
|
||||
{
|
||||
pub(in crate::iter) fn new(iter: I, separator: G) -> Self {
|
||||
Self { started: false, separator, next_item: None, iter: iter.fuse() }
|
||||
Self { started: false, separator, next_item: None, iter }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,8 +157,9 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.started = true;
|
||||
self.iter.next()
|
||||
let item = self.iter.next();
|
||||
self.started = item.is_some();
|
||||
item
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -635,11 +635,24 @@ fn zip<U>(self, other: U) -> Zip<Self, U::IntoIter>
|
||||
/// of the original iterator.
|
||||
///
|
||||
/// Specifically on fused iterators, it is guaranteed that the new iterator
|
||||
/// places a copy of `separator` between adjacent `Some(_)` items. However,
|
||||
/// for non-fused iterators, [`intersperse`] will create a new iterator that
|
||||
/// is a fused version of the original iterator and place a copy of `separator`
|
||||
/// between adjacent `Some(_)` items. This behavior for non-fused iterators
|
||||
/// is subject to change.
|
||||
/// places a copy of `separator` between *adjacent* `Some(_)` items. For non-fused iterators,
|
||||
/// it is guaranteed that [`intersperse`] will create a new iterator that places a copy
|
||||
/// of `separator` between `Some(_)` items, particularly just right before the subsequent
|
||||
/// `Some(_)` item.
|
||||
///
|
||||
/// For example, consider the following non-fused iterator:
|
||||
///
|
||||
/// ```text
|
||||
/// Some(1) -> Some(2) -> None -> Some(3) -> Some(4) -> ...
|
||||
/// ```
|
||||
///
|
||||
/// If this non-fused iterator were to be interspersed with `0`,
|
||||
/// then the interspersed iterator will produce:
|
||||
///
|
||||
/// ```text
|
||||
/// Some(1) -> Some(0) -> Some(2) -> None -> Some(0) -> Some(3) -> Some(0) ->
|
||||
/// Some(4) -> ...
|
||||
/// ```
|
||||
///
|
||||
/// In case `separator` does not implement [`Clone`] or needs to be
|
||||
/// computed every time, use [`intersperse_with`].
|
||||
@@ -688,10 +701,23 @@ fn intersperse(self, separator: Self::Item) -> Intersperse<Self>
|
||||
///
|
||||
/// Specifically on fused iterators, it is guaranteed that the new iterator
|
||||
/// places an item generated by `separator` between adjacent `Some(_)` items.
|
||||
/// However, for non-fused iterators, [`intersperse_with`] will create a new
|
||||
/// iterator that is a fused version of the original iterator and place an item
|
||||
/// generated by `separator` between adjacent `Some(_)` items. This
|
||||
/// behavior for non-fused iterators is subject to change.
|
||||
/// For non-fused iterators, it is guaranteed that [`intersperse_with`] will
|
||||
/// create a new iterator that places an item generated by `separator` between `Some(_)`
|
||||
/// items, particularly just right before the subsequent `Some(_)` item.
|
||||
///
|
||||
/// For example, consider the following non-fused iterator:
|
||||
///
|
||||
/// ```text
|
||||
/// Some(1) -> Some(2) -> None -> Some(3) -> Some(4) -> ...
|
||||
/// ```
|
||||
///
|
||||
/// If this non-fused iterator were to be interspersed with a `separator` closure
|
||||
/// that returns `0` repeatedly, the interspersed iterator will produce:
|
||||
///
|
||||
/// ```text
|
||||
/// Some(1) -> Some(0) -> Some(2) -> None -> Some(0) -> Some(3) -> Some(0) ->
|
||||
/// Some(4) -> ...
|
||||
/// ```
|
||||
///
|
||||
/// The `separator` closure will be called exactly once each time an item
|
||||
/// is placed between two adjacent items from the underlying iterator;
|
||||
|
||||
@@ -153,10 +153,6 @@ fn test_try_fold_specialization_intersperse_err() {
|
||||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
|
||||
// FIXME(iter_intersperse): `intersperse` current behavior may change for
|
||||
// non-fused iterators, so this test will likely have to
|
||||
// be adjusted; see PR #152855 and issue #79524
|
||||
// if `intersperse` doesn't change, remove this FIXME.
|
||||
#[test]
|
||||
fn test_non_fused_iterator_intersperse() {
|
||||
#[derive(Debug)]
|
||||
@@ -183,24 +179,26 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
}
|
||||
|
||||
let counter = 0;
|
||||
// places a 2 between `Some(_)` items
|
||||
// places a 1 between `Some(_)` items
|
||||
let non_fused_iter = TestCounter { counter };
|
||||
let mut intersperse_iter = non_fused_iter.intersperse(2);
|
||||
// Since `intersperse` currently transforms the original
|
||||
// iterator into a fused iterator, this intersperse_iter
|
||||
// should always have `None`
|
||||
for _ in 0..counter + 6 {
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
}
|
||||
let mut intersperse_iter = non_fused_iter.intersperse(1);
|
||||
// Interspersed iter produces:
|
||||
// `None` -> `Some(2)` -> `None` -> `Some(1)` -> Some(4)` -> `None` -> `Some(1)` ->
|
||||
// `Some(6)` -> and then `None` endlessly
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
assert_eq!(intersperse_iter.next(), Some(2));
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
assert_eq!(intersperse_iter.next(), Some(1));
|
||||
assert_eq!(intersperse_iter.next(), Some(4));
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
assert_eq!(intersperse_iter.next(), Some(1));
|
||||
assert_eq!(intersperse_iter.next(), Some(6));
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
|
||||
// Extra check to make sure it is `None` after processing 6 items
|
||||
// Extra check to make sure it is `None` after processing all items
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
}
|
||||
|
||||
// FIXME(iter_intersperse): `intersperse` current behavior may change for
|
||||
// non-fused iterators, so this test will likely have to
|
||||
// be adjusted; see PR #152855 and issue #79524
|
||||
// if `intersperse` doesn't change, remove this FIXME.
|
||||
#[test]
|
||||
fn test_non_fused_iterator_intersperse_2() {
|
||||
#[derive(Debug)]
|
||||
@@ -228,35 +226,26 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
}
|
||||
|
||||
let counter = 0;
|
||||
// places a 2 between `Some(_)` items
|
||||
// places a 100 between `Some(_)` items
|
||||
let non_fused_iter = TestCounter { counter };
|
||||
let mut intersperse_iter = non_fused_iter.intersperse(2);
|
||||
// Since `intersperse` currently transforms the original
|
||||
// iterator into a fused iterator, this interspersed iter
|
||||
// will be `Some(1)` -> `Some(2)` -> `Some(2)` -> and then
|
||||
// `None` endlessly
|
||||
let mut items_processed = 0;
|
||||
for num in 0..counter + 6 {
|
||||
if num < 3 {
|
||||
if num % 2 != 0 {
|
||||
assert_eq!(intersperse_iter.next(), Some(2));
|
||||
} else {
|
||||
items_processed += 1;
|
||||
assert_eq!(intersperse_iter.next(), Some(items_processed));
|
||||
}
|
||||
} else {
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
}
|
||||
}
|
||||
let mut intersperse_iter = non_fused_iter.intersperse(100);
|
||||
// Interspersed iter produces:
|
||||
// `Some(1)` -> `Some(100)` -> `Some(2)` -> `None` -> `Some(100)`
|
||||
// -> `Some(4)` -> `Some(100)` -> `Some(5)` -> `None` endlessly
|
||||
assert_eq!(intersperse_iter.next(), Some(1));
|
||||
assert_eq!(intersperse_iter.next(), Some(100));
|
||||
assert_eq!(intersperse_iter.next(), Some(2));
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
assert_eq!(intersperse_iter.next(), Some(100));
|
||||
assert_eq!(intersperse_iter.next(), Some(4));
|
||||
assert_eq!(intersperse_iter.next(), Some(100));
|
||||
assert_eq!(intersperse_iter.next(), Some(5));
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
|
||||
// Extra check to make sure it is `None` after processing 6 items
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
}
|
||||
|
||||
// FIXME(iter_intersperse): `intersperse_with` current behavior may change for
|
||||
// non-fused iterators, so this test will likely have to
|
||||
// be adjusted; see PR #152855 and issue #79524
|
||||
// if `intersperse_with` doesn't change, remove this FIXME.
|
||||
#[test]
|
||||
fn test_non_fused_iterator_intersperse_with() {
|
||||
#[derive(Debug)]
|
||||
@@ -285,22 +274,24 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
let counter = 0;
|
||||
let non_fused_iter = TestCounter { counter };
|
||||
// places a 2 between `Some(_)` items
|
||||
let mut intersperse_iter = non_fused_iter.intersperse_with(|| 2);
|
||||
// Since `intersperse` currently transforms the original
|
||||
// iterator into a fused iterator, this intersperse_iter
|
||||
// should always have `None`
|
||||
for _ in 0..counter + 6 {
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
}
|
||||
let mut intersperse_iter = non_fused_iter.intersperse_with(|| 1);
|
||||
// Interspersed iter produces:
|
||||
// `None` -> `Some(2)` -> `None` -> `Some(1)` -> Some(4)` -> `None` -> `Some(1)` ->
|
||||
// `Some(6)` -> and then `None` endlessly
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
assert_eq!(intersperse_iter.next(), Some(2));
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
assert_eq!(intersperse_iter.next(), Some(1));
|
||||
assert_eq!(intersperse_iter.next(), Some(4));
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
assert_eq!(intersperse_iter.next(), Some(1));
|
||||
assert_eq!(intersperse_iter.next(), Some(6));
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
|
||||
// Extra check to make sure it is `None` after processing 6 items
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
}
|
||||
|
||||
// FIXME(iter_intersperse): `intersperse_with` current behavior may change for
|
||||
// non-fused iterators, so this test will likely have to
|
||||
// be adjusted; see PR #152855 and issue #79524
|
||||
// if `intersperse_with` doesn't change, remove this FIXME.
|
||||
#[test]
|
||||
fn test_non_fused_iterator_intersperse_with_2() {
|
||||
#[derive(Debug)]
|
||||
@@ -328,26 +319,21 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
}
|
||||
|
||||
let counter = 0;
|
||||
// places a 2 between `Some(_)` items
|
||||
// places a 100 between `Some(_)` items
|
||||
let non_fused_iter = TestCounter { counter };
|
||||
let mut intersperse_iter = non_fused_iter.intersperse(2);
|
||||
// Since `intersperse` currently transforms the original
|
||||
// iterator into a fused iterator, this interspersed iter
|
||||
// will be `Some(1)` -> `Some(2)` -> `Some(2)` -> and then
|
||||
// `None` endlessly
|
||||
let mut items_processed = 0;
|
||||
for num in 0..counter + 6 {
|
||||
if num < 3 {
|
||||
if num % 2 != 0 {
|
||||
assert_eq!(intersperse_iter.next(), Some(2));
|
||||
} else {
|
||||
items_processed += 1;
|
||||
assert_eq!(intersperse_iter.next(), Some(items_processed));
|
||||
}
|
||||
} else {
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
}
|
||||
}
|
||||
let mut intersperse_iter = non_fused_iter.intersperse_with(|| 100);
|
||||
// Interspersed iter produces:
|
||||
// `Some(1)` -> `Some(100)` -> `Some(2)` -> `None` -> `Some(100)`
|
||||
// -> `Some(4)` -> `Some(100)` -> `Some(5)` -> `None` endlessly
|
||||
assert_eq!(intersperse_iter.next(), Some(1));
|
||||
assert_eq!(intersperse_iter.next(), Some(100));
|
||||
assert_eq!(intersperse_iter.next(), Some(2));
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
assert_eq!(intersperse_iter.next(), Some(100));
|
||||
assert_eq!(intersperse_iter.next(), Some(4));
|
||||
assert_eq!(intersperse_iter.next(), Some(100));
|
||||
assert_eq!(intersperse_iter.next(), Some(5));
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
|
||||
// Extra check to make sure it is `None` after processing 6 items
|
||||
assert_eq!(intersperse_iter.next(), None);
|
||||
|
||||
Reference in New Issue
Block a user