From 48aae09e9fe0a5574f59867610a9005c68ff5c19 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sun, 11 Nov 2018 12:13:59 +0100 Subject: [PATCH 1/6] Add std::iter::unfold --- src/libcore/iter/mod.rs | 2 + src/libcore/iter/sources.rs | 76 +++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 509068843d193..5ac8b2d28956a 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -339,6 +339,8 @@ pub use self::sources::{RepeatWith, repeat_with}; pub use self::sources::{Empty, empty}; #[stable(feature = "iter_once", since = "1.2.0")] pub use self::sources::{Once, once}; +#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +pub use self::sources::{Unfold, unfold}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::traits::{FromIterator, IntoIterator, DoubleEndedIterator, Extend}; diff --git a/src/libcore/iter/sources.rs b/src/libcore/iter/sources.rs index 7fa3a4bcce7bb..a209b90ace19b 100644 --- a/src/libcore/iter/sources.rs +++ b/src/libcore/iter/sources.rs @@ -386,3 +386,79 @@ impl FusedIterator for Once {} pub fn once(value: T) -> Once { Once { inner: Some(value).into_iter() } } + +/// Creates a new iterator where each iteration calls the provided closure +/// `F: FnMut(&mut St) -> Option`. +/// +/// This allows creating a custom iterator with any behavior +/// without using the more verbose syntax of creating a dedicated type +/// and implementing the `Iterator` trait for it. +/// +/// In addition to its captures and environment, +/// the closure is given a mutable reference to some state +/// that is preserved across iterations. +/// That state starts as the given `initial_state` value. +/// +/// Note that the `Unfold` iterator doesn’t make assumptions about the behavior of the closure, +/// and therefore conservatively does not implement [`FusedIterator`], +/// or override [`Iterator::size_hint`] from its default `(0, None)`. +/// +/// [`FusedIterator`]: trait.FusedIterator.html +/// [`Iterator::size_hint`]: trait.Iterator.html#method.size_hint +/// +/// # Examples +/// +/// Let’s re-implement the counter iterator from [module-level documentation]: +/// +/// [module-level documentation]: index.html +/// +/// ``` +/// #![feature(iter_unfold)] +/// let counter = std::iter::unfold(0, |count| { +/// // increment our count. This is why we started at zero. +/// *count += 1; +/// +/// // check to see if we've finished counting or not. +/// if *count < 6 { +/// Some(*count) +/// } else { +/// None +/// } +/// }); +/// assert_eq!(counter.collect::>(), &[1, 2, 3, 4, 5]); +/// ``` +#[inline] +#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +pub fn unfold(initial_state: St, f: F) -> Unfold + where F: FnMut(&mut St) -> Option +{ + Unfold { + state: initial_state, + f, + } +} + +/// An iterator where each iteration calls the provided closure `F: FnMut(&mut St) -> Option`. +/// +/// This `struct` is created by the [`unfold`] function. +/// See its documentation for more. +/// +/// [`unfold`]: fn.unfold.html +#[derive(Copy, Clone, Debug)] +#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +pub struct Unfold { + state: St, + f: F, +} + +#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +impl Iterator for Unfold + where F: FnMut(&mut St) -> Option +{ + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + (self.f)(&mut self.state) + } +} From 544ad37753184c387fd235dea8bd4c3fba22bd74 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 15 Nov 2018 13:21:25 +0100 Subject: [PATCH 2/6] Unfold: Debug without F: Debug --- src/libcore/iter/sources.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/libcore/iter/sources.rs b/src/libcore/iter/sources.rs index a209b90ace19b..d769c9c80a078 100644 --- a/src/libcore/iter/sources.rs +++ b/src/libcore/iter/sources.rs @@ -444,7 +444,7 @@ pub fn unfold(initial_state: St, f: F) -> Unfold /// See its documentation for more. /// /// [`unfold`]: fn.unfold.html -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone)] #[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] pub struct Unfold { state: St, @@ -462,3 +462,12 @@ impl Iterator for Unfold (self.f)(&mut self.state) } } + +#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +impl fmt::Debug for Unfold { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Unfold") + .field("state", &self.state) + .finish() + } +} From 22228186c0d373774a117375c2c264024bfd4b48 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 15 Nov 2018 14:22:21 +0100 Subject: [PATCH 3/6] `Copy` is best avoided on iterators --- src/libcore/iter/sources.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/iter/sources.rs b/src/libcore/iter/sources.rs index d769c9c80a078..93c64c748c97b 100644 --- a/src/libcore/iter/sources.rs +++ b/src/libcore/iter/sources.rs @@ -444,7 +444,7 @@ pub fn unfold(initial_state: St, f: F) -> Unfold /// See its documentation for more. /// /// [`unfold`]: fn.unfold.html -#[derive(Copy, Clone)] +#[derive(Clone)] #[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] pub struct Unfold { state: St, From 641c4909e4de0334c83372b4795388d98cfd257a Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 15 Nov 2018 14:23:20 +0100 Subject: [PATCH 4/6] Add std::iter::successors --- src/libcore/iter/mod.rs | 2 +- src/libcore/iter/sources.rs | 76 +++++++++++++++++++++++++++++++++++++ src/libcore/tests/iter.rs | 11 ++++++ src/libcore/tests/lib.rs | 1 + 4 files changed, 89 insertions(+), 1 deletion(-) diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 5ac8b2d28956a..5f45cd927b897 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -340,7 +340,7 @@ pub use self::sources::{Empty, empty}; #[stable(feature = "iter_once", since = "1.2.0")] pub use self::sources::{Once, once}; #[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] -pub use self::sources::{Unfold, unfold}; +pub use self::sources::{Unfold, unfold, Successors, successors}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::traits::{FromIterator, IntoIterator, DoubleEndedIterator, Extend}; diff --git a/src/libcore/iter/sources.rs b/src/libcore/iter/sources.rs index 93c64c748c97b..7f55965239222 100644 --- a/src/libcore/iter/sources.rs +++ b/src/libcore/iter/sources.rs @@ -471,3 +471,79 @@ impl fmt::Debug for Unfold { .finish() } } + +/// Creates a new iterator where each successive item is computed based on the preceding one. +/// +/// The iterator starts with the given first item (if any) +/// and calls the given `FnMut(&T) -> Option` closure to compute each item’s successor. +/// +/// ``` +/// #![feature(iter_unfold)] +/// use std::iter::successors; +/// +/// let powers_of_10 = successors(Some(1_u16), |n| n.checked_mul(10)); +/// assert_eq!(powers_of_10.collect::>(), &[1, 10, 100, 1_000, 10_000]); +/// ``` +#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +pub fn successors(first: Option, succ: F) -> Successors + where F: FnMut(&T) -> Option +{ + // If this function returned `impl Iterator` + // it could be based on `unfold` and not need a dedicated type. + // However having a named `Successors` type allows it to be `Clone` when `T` and `F` are. + Successors { + next: first, + succ, + } +} + +/// An new iterator where each successive item is computed based on the preceding one. +/// +/// This `struct` is created by the [`successors`] function. +/// See its documentation for more. +/// +/// [`successors`]: fn.successors.html +#[derive(Clone)] +#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +pub struct Successors { + next: Option, + succ: F, +} + +#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +impl Iterator for Successors + where F: FnMut(&T) -> Option +{ + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.next.take().map(|item| { + self.next = (self.succ)(&item); + item + }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.next.is_some() { + (1, None) + } else { + (0, Some(0)) + } + } +} + +#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +impl FusedIterator for Successors + where F: FnMut(&T) -> Option +{} + +#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +impl fmt::Debug for Successors { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Successors") + .field("next", &self.next) + .finish() + } +} diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index ec09071b3d0f0..495483db5551c 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -1759,6 +1759,17 @@ fn test_repeat_with_take_collect() { assert_eq!(v, vec![1, 2, 4, 8, 16]); } +#[test] +fn test_successors() { + let mut powers_of_10 = successors(Some(1_u16), |n| n.checked_mul(10)); + assert_eq!(powers_of_10.by_ref().collect::>(), &[1, 10, 100, 1_000, 10_000]); + assert_eq!(powers_of_10.next(), None); + + let mut empty = successors(None::, |_| unimplemented!()); + assert_eq!(empty.next(), None); + assert_eq!(empty.next(), None); +} + #[test] fn test_fuse() { let mut it = 0..3; diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 5ac8991226898..7d62b4fa90f20 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -19,6 +19,7 @@ #![feature(flt2dec)] #![feature(fmt_internals)] #![feature(hashmap_internals)] +#![feature(iter_unfold)] #![feature(pattern)] #![feature(range_is_empty)] #![feature(raw)] From 8a5bbd9a4e85e1383b87796f871a9020410bbd10 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 15 Nov 2018 14:33:47 +0100 Subject: [PATCH 5/6] Add tracking issue for unfold and successors --- src/libcore/iter/mod.rs | 2 +- src/libcore/iter/sources.rs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 5f45cd927b897..8ae4b53da95be 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -339,7 +339,7 @@ pub use self::sources::{RepeatWith, repeat_with}; pub use self::sources::{Empty, empty}; #[stable(feature = "iter_once", since = "1.2.0")] pub use self::sources::{Once, once}; -#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +#[unstable(feature = "iter_unfold", issue = "55977")] pub use self::sources::{Unfold, unfold, Successors, successors}; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/iter/sources.rs b/src/libcore/iter/sources.rs index 7f55965239222..6d35d54641531 100644 --- a/src/libcore/iter/sources.rs +++ b/src/libcore/iter/sources.rs @@ -428,7 +428,7 @@ pub fn once(value: T) -> Once { /// assert_eq!(counter.collect::>(), &[1, 2, 3, 4, 5]); /// ``` #[inline] -#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +#[unstable(feature = "iter_unfold", issue = "55977")] pub fn unfold(initial_state: St, f: F) -> Unfold where F: FnMut(&mut St) -> Option { @@ -445,13 +445,13 @@ pub fn unfold(initial_state: St, f: F) -> Unfold /// /// [`unfold`]: fn.unfold.html #[derive(Clone)] -#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +#[unstable(feature = "iter_unfold", issue = "55977")] pub struct Unfold { state: St, f: F, } -#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +#[unstable(feature = "iter_unfold", issue = "55977")] impl Iterator for Unfold where F: FnMut(&mut St) -> Option { @@ -463,7 +463,7 @@ impl Iterator for Unfold } } -#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +#[unstable(feature = "iter_unfold", issue = "55977")] impl fmt::Debug for Unfold { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Unfold") @@ -484,7 +484,7 @@ impl fmt::Debug for Unfold { /// let powers_of_10 = successors(Some(1_u16), |n| n.checked_mul(10)); /// assert_eq!(powers_of_10.collect::>(), &[1, 10, 100, 1_000, 10_000]); /// ``` -#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +#[unstable(feature = "iter_unfold", issue = "55977")] pub fn successors(first: Option, succ: F) -> Successors where F: FnMut(&T) -> Option { @@ -504,13 +504,13 @@ pub fn successors(first: Option, succ: F) -> Successors /// /// [`successors`]: fn.successors.html #[derive(Clone)] -#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +#[unstable(feature = "iter_unfold", issue = "55977")] pub struct Successors { next: Option, succ: F, } -#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +#[unstable(feature = "iter_unfold", issue = "55977")] impl Iterator for Successors where F: FnMut(&T) -> Option { @@ -534,12 +534,12 @@ impl Iterator for Successors } } -#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +#[unstable(feature = "iter_unfold", issue = "55977")] impl FusedIterator for Successors where F: FnMut(&T) -> Option {} -#[unstable(feature = "iter_unfold", issue = /* FIXME */ "0")] +#[unstable(feature = "iter_unfold", issue = "55977")] impl fmt::Debug for Successors { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Successors") From a4279a07e29091fd8a72b13b2109c8969e713ffd Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 20 Nov 2018 18:22:26 +0100 Subject: [PATCH 6/6] Capitalize --- src/libcore/iter/mod.rs | 4 ++-- src/libcore/iter/sources.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 8ae4b53da95be..62e1f9fcb640c 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -112,10 +112,10 @@ //! //! // next() is the only required method //! fn next(&mut self) -> Option { -//! // increment our count. This is why we started at zero. +//! // Increment our count. This is why we started at zero. //! self.count += 1; //! -//! // check to see if we've finished counting or not. +//! // Check to see if we've finished counting or not. //! if self.count < 6 { //! Some(self.count) //! } else { diff --git a/src/libcore/iter/sources.rs b/src/libcore/iter/sources.rs index 6d35d54641531..f6a4a7a6fa80a 100644 --- a/src/libcore/iter/sources.rs +++ b/src/libcore/iter/sources.rs @@ -415,10 +415,10 @@ pub fn once(value: T) -> Once { /// ``` /// #![feature(iter_unfold)] /// let counter = std::iter::unfold(0, |count| { -/// // increment our count. This is why we started at zero. +/// // Increment our count. This is why we started at zero. /// *count += 1; /// -/// // check to see if we've finished counting or not. +/// // Check to see if we've finished counting or not. /// if *count < 6 { /// Some(*count) /// } else {