Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add std/core::iter::repeat_with #48156

Merged
merged 9 commits into from
Feb 15, 2018
2 changes: 2 additions & 0 deletions src/libcore/iter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,8 @@ pub use self::range::Step;

#[stable(feature = "rust1", since = "1.0.0")]
pub use self::sources::{Repeat, repeat};
#[unstable(feature = "iterator_repeat_with", issue = "48169")]
pub use self::sources::{RepeatWith, repeat_with};
#[stable(feature = "iter_empty", since = "1.2.0")]
pub use self::sources::{Empty, empty};
#[stable(feature = "iter_once", since = "1.2.0")]
Expand Down
115 changes: 115 additions & 0 deletions src/libcore/iter/sources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ unsafe impl<A: Clone> TrustedLen for Repeat<A> {}
///
/// [`take`]: trait.Iterator.html#method.take
///
/// If the element type of the iterator you need does not implement `Clone`,
/// or if you do not want to keep the repeated element in memory, you can
/// instead use the [`repeat_with`] function.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary that we mention it, but another case is indeed to emit a different value each time the function is called, for example (stolen from itertools so it uses while_some() as well):

let mut heap = BinaryHeap::from(vec![2, 5, 3, 7, 8]);

// extract each element in sorted order
for element in repeat_with(|| heap.pop()).while_some() {
    print!("{}", element);
}

Copy link
Contributor Author

@Centril Centril Feb 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty neat example - let's have .while_some() in core?

Edit: we could also have a while_some(..) source? so that you can write:

for elt in while_some(|| heap.pop()) {
    print!("{}", elt);
}

I think while_some is a pretty descriptive name as source as well.

Copy link
Member

@scottmcm scottmcm Feb 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully there's a Try version that can go well with Option and Result and friends, or perhaps a more fundamental adapter too. Possibly-related: #45594 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@scottmcm Perhaps Try<Ok = Elt, Error: Into<()>>?

///
/// [`repeat_with`]: fn.repeat_with.html
///
/// # Examples
///
/// Basic usage:
Expand Down Expand Up @@ -99,6 +105,115 @@ pub fn repeat<T: Clone>(elt: T) -> Repeat<T> {
Repeat{element: elt}
}

/// An iterator that repeats elements of type `A` endlessly by
/// applying the provided closure `F: FnMut() -> A`.
///
/// This `struct` is created by the [`repeat_with`] function.
/// See its documentation for more.
///
/// [`repeat_with`]: fn.repeat_with.html
#[derive(Copy, Clone, Debug)]
#[unstable(feature = "iterator_repeat_with", issue = "48169")]
pub struct RepeatWith<F> {
repeater: F
}

#[unstable(feature = "iterator_repeat_with", issue = "48169")]
impl<A, F: FnMut() -> A> Iterator for RepeatWith<F> {
type Item = A;

#[inline]
fn next(&mut self) -> Option<A> { Some((self.repeater)()) }

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) { (usize::MAX, None) }
}

#[unstable(feature = "iterator_repeat_with", issue = "48169")]
impl<A, F: FnMut() -> A> DoubleEndedIterator for RepeatWith<F> {
#[inline]
fn next_back(&mut self) -> Option<A> { self.next() }
}

#[unstable(feature = "fused", issue = "35602")]
impl<A, F: FnMut() -> A> FusedIterator for RepeatWith<F> {}

#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<A, F: FnMut() -> A> TrustedLen for RepeatWith<F> {}

/// Creates a new iterator that repeats elements of type `A` endlessly by
/// applying the provided closure, the repeater, `F: FnMut() -> A`.
///
/// The `repeat_with()` function calls the repeater over and over and over and
/// over and over and 🔁.
///
/// Infinite iterators like `repeat_with()` are often used with adapters like
/// [`take`], in order to make them finite.
///
/// [`take`]: trait.Iterator.html#method.take
///
/// If the element type of the iterator you need implements `Clone`, and
/// it is OK to keep the source element in memory, you should instead use
/// the [`repeat`] function.
///
/// [`repeat`]: fn.repeat.html
///
/// An iterator produced by `repeat_with()` is a `DoubleEndedIterator`.
/// It is important to not that reversing `repeat_with(f)` will produce
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that not should be a note instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #48282.

/// the exact same sequence as the non-reversed iterator. In other words,
/// `repeat_with(f).rev().collect::<Vec<_>>()` is equivalent to
/// `repeat_with(f).collect::<Vec<_>>()`.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(iterator_repeat_with)]
///
/// use std::iter;
///
/// // let's assume we have some value of a type that is not `Clone`
/// // or which don't want to have in memory just yet because it is expensive:
/// #[derive(PartialEq, Debug)]
/// struct Expensive;
///
/// // a particular value forever:
/// let mut things = iter::repeat_with(|| Expensive);
///
/// assert_eq!(Some(Expensive), things.next());
/// assert_eq!(Some(Expensive), things.next());
/// assert_eq!(Some(Expensive), things.next());
/// assert_eq!(Some(Expensive), things.next());
/// assert_eq!(Some(Expensive), things.next());
/// ```
///
/// Using mutation and going finite:
///
/// ```rust
/// #![feature(iterator_repeat_with)]
///
/// use std::iter;
///
/// // From the zeroth to the third power of two:
/// let mut curr = 1;
/// let mut pow2 = iter::repeat_with(|| { let tmp = curr; curr *= 2; tmp })
/// .take(4);
///
/// assert_eq!(Some(1), pow2.next());
/// assert_eq!(Some(2), pow2.next());
/// assert_eq!(Some(4), pow2.next());
/// assert_eq!(Some(8), pow2.next());
///
/// // ... and now we're done
/// assert_eq!(None, pow2.next());
/// ```
#[inline]
#[unstable(feature = "iterator_repeat_with", issue = "48169")]
pub fn repeat_with<A, F: FnMut() -> A>(repeater: F) -> RepeatWith<F> {
RepeatWith { repeater }
}

/// An iterator that yields nothing.
///
/// This `struct` is created by the [`empty`] function. See its documentation for more.
Expand Down
1 change: 1 addition & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
#![feature(unwind_attributes)]
#![feature(doc_spotlight)]
#![feature(rustc_const_unstable)]
#![feature(iterator_repeat_with)]

#[prelude_import]
#[allow(unused)]
Expand Down
45 changes: 45 additions & 0 deletions src/libcore/tests/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1549,6 +1549,51 @@ fn test_repeat_take_collect() {
assert_eq!(v, vec![42, 42, 42]);
}

#[test]
fn test_repeat_with() {
#[derive(PartialEq, Debug)]
struct NotClone(usize);
let mut it = repeat_with(|| NotClone(42));
assert_eq!(it.next(), Some(NotClone(42)));
assert_eq!(it.next(), Some(NotClone(42)));
assert_eq!(it.next(), Some(NotClone(42)));
assert_eq!(repeat_with(|| NotClone(42)).size_hint(), (usize::MAX, None));
}

#[test]
fn test_repeat_with_rev() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This behaviour is surprising to me, since it produces the same sequence of different things with or without the .rev(). Perhaps it should only be DEI for F:Fn, not F:FnMut?

Copy link
Contributor Author

@Centril Centril Feb 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I agree. F: Fn() -> A seems reasonable under the assumption that it should be a pure function in the context of iterators, and we can always document that on repeat_with (the function).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's consistent with map this way... No big opinion but I'd vote to keep it double ended with FnMut

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bluss I guess that works too if we document it well... I don't have a big opinion either.

let mut curr = 1;
let mut pow2 = repeat_with(|| { let tmp = curr; curr *= 2; tmp })
.rev().take(4);
assert_eq!(pow2.next(), Some(1));
assert_eq!(pow2.next(), Some(2));
assert_eq!(pow2.next(), Some(4));
assert_eq!(pow2.next(), Some(8));
assert_eq!(pow2.next(), None);
}

#[test]
fn test_repeat_with_take() {
let mut it = repeat_with(|| 42).take(3);
assert_eq!(it.next(), Some(42));
assert_eq!(it.next(), Some(42));
assert_eq!(it.next(), Some(42));
assert_eq!(it.next(), None);
is_trusted_len(repeat_with(|| 42).take(3));
assert_eq!(repeat_with(|| 42).take(3).size_hint(), (3, Some(3)));
assert_eq!(repeat_with(|| 42).take(0).size_hint(), (0, Some(0)));
assert_eq!(repeat_with(|| 42).take(usize::MAX).size_hint(),
(usize::MAX, Some(usize::MAX)));
}

#[test]
fn test_repeat_with_take_collect() {
let mut curr = 1;
let v: Vec<_> = repeat_with(|| { let tmp = curr; curr *= 2; tmp })
.take(5).collect();
assert_eq!(v, vec![1, 2, 4, 8, 16]);
}

#[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 @@ -27,6 +27,7 @@
#![feature(iterator_try_fold)]
#![feature(iter_rfind)]
#![feature(iter_rfold)]
#![feature(iterator_repeat_with)]
#![feature(nonzero)]
#![feature(pattern)]
#![feature(raw)]
Expand Down