From 13d5e6d016f7ee2653f567bf219ec87db893e733 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 21 Aug 2020 21:56:40 +0200 Subject: [PATCH 1/8] Add Iterator::intersperse --- library/core/src/iter/adapters/intersperse.rs | 62 +++++++++++++++++++ library/core/src/iter/adapters/mod.rs | 3 + library/core/src/iter/mod.rs | 3 +- library/core/src/iter/traits/iterator.rs | 24 ++++++- src/librustdoc/clean/utils.rs | 1 - src/librustdoc/lib.rs | 1 + 6 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 library/core/src/iter/adapters/intersperse.rs diff --git a/library/core/src/iter/adapters/intersperse.rs b/library/core/src/iter/adapters/intersperse.rs new file mode 100644 index 0000000000000..d973ece34fbfa --- /dev/null +++ b/library/core/src/iter/adapters/intersperse.rs @@ -0,0 +1,62 @@ +use crate::iter::Fuse; + +/// An iterator adapter that places a separator between all elements. +#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "none")] +#[derive(Debug, Clone)] +pub struct Intersperse +where + I::Item: Clone, +{ + element: I::Item, + iter: Fuse, + peek: Option, +} + +impl Intersperse +where + I::Item: Clone, +{ + pub(in super::super) fn new(iter: I, separator: I::Item) -> Self { + let mut iter = iter.fuse(); + Self { peek: iter.next(), iter, element: separator } + } +} + +#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "none")] +impl Iterator for Intersperse +where + I: Iterator, + I::Item: Clone, +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + if self.peek.is_some() { + self.peek.take() + } else { + self.peek = self.iter.next(); + if self.peek.is_some() { Some(self.element.clone()) } else { None } + } + } + + fn fold(mut self, init: B, mut f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + let mut accum = init; + + if let Some(x) = self.peek.take() { + accum = f(accum, x); + } + + let element = &self.element; + + self.iter.fold(accum, |accum, x| { + let accum = f(accum, element.clone()); + let accum = f(accum, x); + accum + }) + } +} diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index ab27fe15a8e2c..1a66769f02505 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -11,12 +11,15 @@ use super::{ mod chain; mod flatten; mod fuse; +mod intersperse; mod zip; pub use self::chain::Chain; #[stable(feature = "rust1", since = "1.0.0")] pub use self::flatten::{FlatMap, Flatten}; pub use self::fuse::Fuse; +#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "none")] +pub use self::intersperse::Intersperse; use self::zip::try_get_unchecked; #[unstable(feature = "trusted_random_access", issue = "none")] pub use self::zip::TrustedRandomAccess; diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index 59f333e888b88..bf44c35ecc2b5 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -351,7 +351,8 @@ pub use self::adapters::Cloned; pub use self::adapters::Copied; #[stable(feature = "iterator_flatten", since = "1.29.0")] pub use self::adapters::Flatten; - +#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "none")] +pub use self::adapters::Intersperse; #[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] pub use self::adapters::MapWhile; #[unstable(issue = "none", feature = "inplace_iteration")] diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index b8a09f822b6da..051923e881e1b 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -8,7 +8,7 @@ use crate::ops::{Add, ControlFlow, Try}; use super::super::TrustedRandomAccess; use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; use super::super::{FlatMap, Flatten}; -use super::super::{FromIterator, Product, Sum, Zip}; +use super::super::{FromIterator, Intersperse, Product, Sum, Zip}; use super::super::{ Inspect, Map, MapWhile, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile, }; @@ -536,6 +536,28 @@ pub trait Iterator { Zip::new(self, other.into_iter()) } + /// Places a copy of `separator` between all elements. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iter_intersperse)] + /// + /// let hello = ["Hello", "World"].iter().map(|s| *s).intersperse(" ").collect::(); + /// assert_eq!(hello, "Hello World"); + /// ``` + #[inline] + #[unstable(feature = "iter_intersperse", reason = "recently added", issue = "none")] + fn intersperse(self, separator: Self::Item) -> Intersperse + where + Self: Sized, + Self::Item: Clone, + { + Intersperse::new(self, separator) + } + /// Takes a closure and creates an iterator which calls that closure on each /// element. /// diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index c577b771d6094..f3e169fe20e2a 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -8,7 +8,6 @@ use crate::clean::{ }; use crate::core::DocContext; -use itertools::Itertools; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 73a783d54060c..d5459c5cd0344 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -12,6 +12,7 @@ #![feature(crate_visibility_modifier)] #![feature(never_type)] #![feature(once_cell)] +#![feature(iter_intersperse)] #![recursion_limit = "256"] #[macro_use] From 8a05fbc3619d8d87d2d35a4a97ef4ac19ac6852d Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sat, 22 Aug 2020 16:55:37 +0200 Subject: [PATCH 2/8] Update library/core/src/iter/adapters/intersperse.rs Co-authored-by: Ivan Tham --- library/core/src/iter/adapters/intersperse.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/iter/adapters/intersperse.rs b/library/core/src/iter/adapters/intersperse.rs index d973ece34fbfa..48c785dfb5498 100644 --- a/library/core/src/iter/adapters/intersperse.rs +++ b/library/core/src/iter/adapters/intersperse.rs @@ -32,11 +32,11 @@ where #[inline] fn next(&mut self) -> Option { - if self.peek.is_some() { - self.peek.take() + if let Some(item) = self.peek.take() { + Some(item) } else { - self.peek = self.iter.next(); - if self.peek.is_some() { Some(self.element.clone()) } else { None } + self.peek = Some(self.iter.next()?); + Some(self.element.clone()) } } From f470155998fc1f6a69a0c1b02f43acf789596ab1 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sat, 22 Aug 2020 19:00:36 +0200 Subject: [PATCH 3/8] Address review comments --- library/core/src/iter/adapters/intersperse.rs | 2 +- library/core/src/iter/traits/iterator.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/iter/adapters/intersperse.rs b/library/core/src/iter/adapters/intersperse.rs index 48c785dfb5498..de8ff72522537 100644 --- a/library/core/src/iter/adapters/intersperse.rs +++ b/library/core/src/iter/adapters/intersperse.rs @@ -16,7 +16,7 @@ impl Intersperse where I::Item: Clone, { - pub(in super::super) fn new(iter: I, separator: I::Item) -> Self { + pub(in crate::iter) fn new(iter: I, separator: I::Item) -> Self { let mut iter = iter.fuse(); Self { peek: iter.next(), iter, element: separator } } diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 051923e881e1b..3a3687f577bec 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -545,7 +545,7 @@ pub trait Iterator { /// ``` /// #![feature(iter_intersperse)] /// - /// let hello = ["Hello", "World"].iter().map(|s| *s).intersperse(" ").collect::(); + /// let hello = ["Hello", "World"].iter().copied().intersperse(" ").collect::(); /// assert_eq!(hello, "Hello World"); /// ``` #[inline] From 97039d81a71cb1722571eb50a5c3dc63b5fd36a6 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sat, 22 Aug 2020 19:33:12 +0200 Subject: [PATCH 4/8] Implement `size_hint` --- library/core/src/iter/adapters/intersperse.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library/core/src/iter/adapters/intersperse.rs b/library/core/src/iter/adapters/intersperse.rs index de8ff72522537..748da0b8f3e1d 100644 --- a/library/core/src/iter/adapters/intersperse.rs +++ b/library/core/src/iter/adapters/intersperse.rs @@ -59,4 +59,14 @@ where accum }) } + + fn size_hint(&self) -> (usize, Option) { + // We'll yield the whole `self.iter`, and just as many separators, as well as (if present) + // `self.peek`. + let (lo, hi) = self.iter.size_hint(); + let has_peek = self.peek.is_some() as usize; + let lo = lo.saturating_add(lo).saturating_add(has_peek); + let hi = hi.map(|hi| hi.saturating_add(hi).saturating_add(has_peek)); + (lo, hi) + } } From 572b795a7bf9ea3e45c339e4a46c34057b13abb7 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Mon, 24 Aug 2020 23:32:04 +0200 Subject: [PATCH 5/8] Update library/core/src/iter/adapters/intersperse.rs Co-authored-by: Oliver Middleton --- library/core/src/iter/adapters/intersperse.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/iter/adapters/intersperse.rs b/library/core/src/iter/adapters/intersperse.rs index 748da0b8f3e1d..52ffb5ab59182 100644 --- a/library/core/src/iter/adapters/intersperse.rs +++ b/library/core/src/iter/adapters/intersperse.rs @@ -66,7 +66,7 @@ where let (lo, hi) = self.iter.size_hint(); let has_peek = self.peek.is_some() as usize; let lo = lo.saturating_add(lo).saturating_add(has_peek); - let hi = hi.map(|hi| hi.saturating_add(hi).saturating_add(has_peek)); + let hi = hi.and_then(|hi| hi.checked_add(hi)).and_then(|hi| hi.checked_add(has_peek)); (lo, hi) } } From 00ad5c8b36fcf597f27b728e10befce6a45bd230 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sun, 6 Sep 2020 15:56:41 +0200 Subject: [PATCH 6/8] Rewrite and add tests, address remaining comments --- library/core/src/iter/adapters/intersperse.rs | 71 +++++++++++++------ library/core/tests/iter.rs | 36 ++++++++++ library/core/tests/lib.rs | 1 + 3 files changed, 87 insertions(+), 21 deletions(-) diff --git a/library/core/src/iter/adapters/intersperse.rs b/library/core/src/iter/adapters/intersperse.rs index 52ffb5ab59182..5d97dec140304 100644 --- a/library/core/src/iter/adapters/intersperse.rs +++ b/library/core/src/iter/adapters/intersperse.rs @@ -1,4 +1,6 @@ -use crate::iter::Fuse; +use crate::ops::Try; + +use super::Peekable; /// An iterator adapter that places a separator between all elements. #[unstable(feature = "iter_intersperse", reason = "recently added", issue = "none")] @@ -7,9 +9,9 @@ pub struct Intersperse where I::Item: Clone, { - element: I::Item, - iter: Fuse, - peek: Option, + separator: I::Item, + iter: Peekable, + needs_sep: bool, } impl Intersperse @@ -17,8 +19,7 @@ where I::Item: Clone, { pub(in crate::iter) fn new(iter: I, separator: I::Item) -> Self { - let mut iter = iter.fuse(); - Self { peek: iter.next(), iter, element: separator } + Self { iter: iter.peekable(), separator, needs_sep: false } } } @@ -32,11 +33,12 @@ where #[inline] fn next(&mut self) -> Option { - if let Some(item) = self.peek.take() { - Some(item) + if self.needs_sep && self.iter.peek().is_some() { + self.needs_sep = false; + Some(self.separator.clone()) } else { - self.peek = Some(self.iter.next()?); - Some(self.element.clone()) + self.needs_sep = true; + self.iter.next() } } @@ -47,26 +49,53 @@ where { let mut accum = init; - if let Some(x) = self.peek.take() { - accum = f(accum, x); + // Use `peek()` first to avoid calling `next()` on an empty iterator. + if !self.needs_sep || self.iter.peek().is_some() { + if let Some(x) = self.iter.next() { + accum = f(accum, x); + } } - let element = &self.element; + let element = &self.separator; - self.iter.fold(accum, |accum, x| { - let accum = f(accum, element.clone()); - let accum = f(accum, x); + self.iter.fold(accum, |mut accum, x| { + accum = f(accum, element.clone()); + accum = f(accum, x); accum }) } + fn try_fold(&mut self, init: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + let mut accum = init; + + // Use `peek()` first to avoid calling `next()` on an empty iterator. + if !self.needs_sep || self.iter.peek().is_some() { + if let Some(x) = self.iter.next() { + accum = f(accum, x)?; + } + } + + let element = &self.separator; + + self.iter.try_fold(accum, |mut accum, x| { + accum = f(accum, element.clone())?; + accum = f(accum, x)?; + Try::from_ok(accum) + }) + } + fn size_hint(&self) -> (usize, Option) { - // We'll yield the whole `self.iter`, and just as many separators, as well as (if present) - // `self.peek`. let (lo, hi) = self.iter.size_hint(); - let has_peek = self.peek.is_some() as usize; - let lo = lo.saturating_add(lo).saturating_add(has_peek); - let hi = hi.and_then(|hi| hi.checked_add(hi)).and_then(|hi| hi.checked_add(has_peek)); + let next_is_elem = !self.needs_sep; + let lo = lo.saturating_add(lo).saturating_sub(next_is_elem as usize); + let hi = hi + .and_then(|hi| hi.checked_add(hi)) + .and_then(|hi| hi.checked_sub(next_is_elem as usize)); (lo, hi) } } diff --git a/library/core/tests/iter.rs b/library/core/tests/iter.rs index 00e3972c42f9d..e96680f335ca7 100644 --- a/library/core/tests/iter.rs +++ b/library/core/tests/iter.rs @@ -3222,3 +3222,39 @@ fn test_flatten_non_fused_inner() { assert_eq!(iter.next(), Some(1)); assert_eq!(iter.next(), None); } + +#[test] +fn test_intersperse() { + let xs = ["a", "", "b", "c"]; + let v: Vec<&str> = xs.iter().map(|x| x.clone()).intersperse(", ").collect(); + let text: String = v.concat(); + assert_eq!(text, "a, , b, c".to_string()); + + let ys = [0, 1, 2, 3]; + let mut it = ys[..0].iter().map(|x| *x).intersperse(1); + assert!(it.next() == None); +} + +#[test] +fn test_intersperse_size_hint() { + let xs = ["a", "", "b", "c"]; + let mut iter = xs.iter().map(|x| x.clone()).intersperse(", "); + assert_eq!(iter.size_hint(), (7, Some(7))); + + assert_eq!(iter.next(), Some("a")); + assert_eq!(iter.size_hint(), (6, Some(6))); + assert_eq!(iter.next(), Some(", ")); + assert_eq!(iter.size_hint(), (5, Some(5))); +} + +#[test] +fn test_fold_specialization_intersperse() { + let mut iter = (1..2).intersperse(0); + iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); + + let mut iter = (1..3).intersperse(0); + iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); + + let mut iter = (1..4).intersperse(0); + iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 4a651e5aa0ee3..e658c8ec411bb 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -33,6 +33,7 @@ #![feature(int_error_matching)] #![feature(array_value_iter)] #![feature(iter_partition_in_place)] +#![feature(iter_intersperse)] #![feature(iter_is_partitioned)] #![feature(iter_order_by)] #![feature(cmp_min_max_by)] From d3122a83239f2e2b21eeaf49950d51871e5c9c1c Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sat, 12 Sep 2020 00:13:42 +0200 Subject: [PATCH 7/8] Remove incorrect `try_fold` and add more tests --- library/core/src/iter/adapters/intersperse.rs | 26 ----------- library/core/tests/iter.rs | 44 +++++++++++++++++++ 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/library/core/src/iter/adapters/intersperse.rs b/library/core/src/iter/adapters/intersperse.rs index 5d97dec140304..135759554a0ca 100644 --- a/library/core/src/iter/adapters/intersperse.rs +++ b/library/core/src/iter/adapters/intersperse.rs @@ -1,5 +1,3 @@ -use crate::ops::Try; - use super::Peekable; /// An iterator adapter that places a separator between all elements. @@ -65,30 +63,6 @@ where }) } - fn try_fold(&mut self, init: B, mut f: F) -> R - where - Self: Sized, - F: FnMut(B, Self::Item) -> R, - R: Try, - { - let mut accum = init; - - // Use `peek()` first to avoid calling `next()` on an empty iterator. - if !self.needs_sep || self.iter.peek().is_some() { - if let Some(x) = self.iter.next() { - accum = f(accum, x)?; - } - } - - let element = &self.separator; - - self.iter.try_fold(accum, |mut accum, x| { - accum = f(accum, element.clone())?; - accum = f(accum, x)?; - Try::from_ok(accum) - }) - } - fn size_hint(&self) -> (usize, Option) { let (lo, hi) = self.iter.size_hint(); let next_is_elem = !self.needs_sep; diff --git a/library/core/tests/iter.rs b/library/core/tests/iter.rs index e96680f335ca7..d79b9e171d756 100644 --- a/library/core/tests/iter.rs +++ b/library/core/tests/iter.rs @@ -3258,3 +3258,47 @@ fn test_fold_specialization_intersperse() { let mut iter = (1..4).intersperse(0); iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); } + +#[test] +fn test_try_fold_specialization_intersperse_ok() { + let mut iter = (1..2).intersperse(0); + iter.clone().try_for_each(|x| { + assert_eq!(Some(x), iter.next()); + Some(()) + }); + + let mut iter = (1..3).intersperse(0); + iter.clone().try_for_each(|x| { + assert_eq!(Some(x), iter.next()); + Some(()) + }); + + let mut iter = (1..4).intersperse(0); + iter.clone().try_for_each(|x| { + assert_eq!(Some(x), iter.next()); + Some(()) + }); +} + +#[test] +fn test_try_fold_specialization_intersperse_err() { + let orig_iter = ["a", "b"].iter().copied().intersperse("-"); + + // Abort after the first item. + let mut iter = orig_iter.clone(); + iter.try_for_each(|_| None::<()>); + assert_eq!(iter.next(), Some("-")); + assert_eq!(iter.next(), Some("b")); + assert_eq!(iter.next(), None); + + // Abort after the second item. + let mut iter = orig_iter.clone(); + iter.try_for_each(|item| if item == "-" { None } else { Some(()) }); + assert_eq!(iter.next(), Some("b")); + assert_eq!(iter.next(), None); + + // Abort after the third item. + let mut iter = orig_iter.clone(); + iter.try_for_each(|item| if item == "b" { None } else { Some(()) }); + assert_eq!(iter.next(), None); +} From 3a069a03f052f239cf3fa8b26ac69fce5f672bc5 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sat, 12 Sep 2020 01:12:10 +0200 Subject: [PATCH 8/8] Improve `size_hint` --- library/core/src/iter/adapters/intersperse.rs | 9 +++++---- library/core/tests/iter.rs | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/library/core/src/iter/adapters/intersperse.rs b/library/core/src/iter/adapters/intersperse.rs index 135759554a0ca..4656a725a3c61 100644 --- a/library/core/src/iter/adapters/intersperse.rs +++ b/library/core/src/iter/adapters/intersperse.rs @@ -66,10 +66,11 @@ where fn size_hint(&self) -> (usize, Option) { let (lo, hi) = self.iter.size_hint(); let next_is_elem = !self.needs_sep; - let lo = lo.saturating_add(lo).saturating_sub(next_is_elem as usize); - let hi = hi - .and_then(|hi| hi.checked_add(hi)) - .and_then(|hi| hi.checked_sub(next_is_elem as usize)); + let lo = lo.saturating_sub(next_is_elem as usize).saturating_add(lo); + let hi = match hi { + Some(hi) => hi.saturating_sub(next_is_elem as usize).checked_add(hi), + None => None, + }; (lo, hi) } } diff --git a/library/core/tests/iter.rs b/library/core/tests/iter.rs index d79b9e171d756..07c1ebc85002d 100644 --- a/library/core/tests/iter.rs +++ b/library/core/tests/iter.rs @@ -3245,6 +3245,8 @@ fn test_intersperse_size_hint() { assert_eq!(iter.size_hint(), (6, Some(6))); assert_eq!(iter.next(), Some(", ")); assert_eq!(iter.size_hint(), (5, Some(5))); + + assert_eq!([].iter().intersperse(&()).size_hint(), (0, Some(0))); } #[test]