Skip to content

Commit

Permalink
Rollup merge of rust-lang#55869 - SimonSapin:iterate, r=alexcrichton
Browse files Browse the repository at this point in the history
Add std::iter::unfold

This adds an **unstable** ~`std::iter::iterate`~ `std::iter::unfold` function and ~`std::iter::Iterate`~ `std::iter::Unfold` type that trivially wrap a ~`FnMut() -> Option<T>`~ `FnMut(&mut State) -> Option<T>` closure to create an iterator. ~Iterator state can be kept in the closure’s environment or captures.~

This is intended to help reduce amount of boilerplate needed when defining an iterator that is only created in one place. Compare the existing example of the `std::iter` module: (explanatory comments elided)

```rust
struct Counter {
    count: usize,
}

impl Counter {
    fn new() -> Counter {
        Counter { count: 0 }
    }
}

impl Iterator for Counter {
    type Item = usize;

    fn next(&mut self) -> Option<usize> {
        self.count += 1;
        if self.count < 6 {
            Some(self.count)
        } else {
            None
        }
    }
}
```

… with the same algorithm rewritten to use this new API:

```rust
fn counter() -> impl Iterator<Item=usize> {
    std::iter::unfold(0, |count| {
        *count += 1;
        if *count < 6 {
            Some(*count)
        } else {
            None
        }
    })
}
```

-----

This also add unstable `std::iter::successors` which takes an (optional) initial item and a closure that takes an item and computes the next one (its successor).

```rust
let powers_of_10 = successors(Some(1_u16), |n| n.checked_mul(10));
assert_eq!(powers_of_10.collect::<Vec<_>>(), &[1, 10, 100, 1_000, 10_000]);
```
  • Loading branch information
kennytm authored Nov 23, 2018
2 parents 738afd4 + a4279a0 commit ef2cbec
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 2 deletions.
6 changes: 4 additions & 2 deletions src/libcore/iter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,10 @@
//!
//! // next() is the only required method
//! fn next(&mut self) -> Option<usize> {
//! // 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 {
Expand Down Expand Up @@ -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 = "55977")]
pub use self::sources::{Unfold, unfold, Successors, successors};

#[stable(feature = "rust1", since = "1.0.0")]
pub use self::traits::{FromIterator, IntoIterator, DoubleEndedIterator, Extend};
Expand Down
161 changes: 161 additions & 0 deletions src/libcore/iter/sources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,3 +386,164 @@ impl<T> FusedIterator for Once<T> {}
pub fn once<T>(value: T) -> Once<T> {
Once { inner: Some(value).into_iter() }
}

/// Creates a new iterator where each iteration calls the provided closure
/// `F: FnMut(&mut St) -> Option<T>`.
///
/// 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::<Vec<_>>(), &[1, 2, 3, 4, 5]);
/// ```
#[inline]
#[unstable(feature = "iter_unfold", issue = "55977")]
pub fn unfold<St, T, F>(initial_state: St, f: F) -> Unfold<St, F>
where F: FnMut(&mut St) -> Option<T>
{
Unfold {
state: initial_state,
f,
}
}

/// An iterator where each iteration calls the provided closure `F: FnMut(&mut St) -> Option<T>`.
///
/// This `struct` is created by the [`unfold`] function.
/// See its documentation for more.
///
/// [`unfold`]: fn.unfold.html
#[derive(Clone)]
#[unstable(feature = "iter_unfold", issue = "55977")]
pub struct Unfold<St, F> {
state: St,
f: F,
}

#[unstable(feature = "iter_unfold", issue = "55977")]
impl<St, T, F> Iterator for Unfold<St, F>
where F: FnMut(&mut St) -> Option<T>
{
type Item = T;

#[inline]
fn next(&mut self) -> Option<Self::Item> {
(self.f)(&mut self.state)
}
}

#[unstable(feature = "iter_unfold", issue = "55977")]
impl<St: fmt::Debug, F> fmt::Debug for Unfold<St, F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Unfold")
.field("state", &self.state)
.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<T>` 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::<Vec<_>>(), &[1, 10, 100, 1_000, 10_000]);
/// ```
#[unstable(feature = "iter_unfold", issue = "55977")]
pub fn successors<T, F>(first: Option<T>, succ: F) -> Successors<T, F>
where F: FnMut(&T) -> Option<T>
{
// If this function returned `impl Iterator<Item=T>`
// it could be based on `unfold` and not need a dedicated type.
// However having a named `Successors<T, F>` 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 = "55977")]
pub struct Successors<T, F> {
next: Option<T>,
succ: F,
}

#[unstable(feature = "iter_unfold", issue = "55977")]
impl<T, F> Iterator for Successors<T, F>
where F: FnMut(&T) -> Option<T>
{
type Item = T;

#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.next.take().map(|item| {
self.next = (self.succ)(&item);
item
})
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
if self.next.is_some() {
(1, None)
} else {
(0, Some(0))
}
}
}

#[unstable(feature = "iter_unfold", issue = "55977")]
impl<T, F> FusedIterator for Successors<T, F>
where F: FnMut(&T) -> Option<T>
{}

#[unstable(feature = "iter_unfold", issue = "55977")]
impl<T: fmt::Debug, F> fmt::Debug for Successors<T, F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Successors")
.field("next", &self.next)
.finish()
}
}
11 changes: 11 additions & 0 deletions src/libcore/tests/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<_>>(), &[1, 10, 100, 1_000, 10_000]);
assert_eq!(powers_of_10.next(), None);

let mut empty = successors(None::<u32>, |_| unimplemented!());
assert_eq!(empty.next(), None);
assert_eq!(empty.next(), None);
}

#[test]
fn test_fuse() {
let mut it = 0..3;
Expand Down
1 change: 1 addition & 0 deletions src/libcore/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#![feature(flt2dec)]
#![feature(fmt_internals)]
#![feature(hashmap_internals)]
#![feature(iter_unfold)]
#![feature(pattern)]
#![feature(range_is_empty)]
#![feature(raw)]
Expand Down

0 comments on commit ef2cbec

Please sign in to comment.