Skip to content

Commit

Permalink
Auto merge of rust-lang#42782 - cuviper:iterator_for_each, r=alexcric…
Browse files Browse the repository at this point in the history
…hton

Add `Iterator::for_each`

This works like a `for` loop in functional style, applying a closure to
every item in the `Iterator`.  It doesn't allow `break`/`continue` like
a `for` loop, nor any other control flow outside the closure, but it may
be a more legible style for tying up the end of a long iterator chain.

This was tried before in rust-lang#14911, but nobody made the case for using it
with longer iterators.  There was also `Iterator::advance` at that time
which was more capable than `for_each`, but that no longer exists.

The `itertools` crate has `Itertools::foreach` with the same behavior,
but thankfully the names won't collide.  The `rayon` crate also has a
`ParallelIterator::for_each` where simple `for` loops aren't possible.

> I really wish we had `for_each` on seq iterators. Having to use a
> dummy operation is annoying.  - [@nikomatsakis][1]

[1]: https://github.com/nikomatsakis/rayon/pull/367#issuecomment-308455185
  • Loading branch information
bors committed Jun 30, 2017
2 parents 4c5b437 + e72ee6e commit 919c4a6
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 0 deletions.
17 changes: 17 additions & 0 deletions src/doc/unstable-book/src/library-features/iterator-for-each.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# `iterator_for_each`

The tracking issue for this feature is: [#TBD]

[#TBD]: https://github.com/rust-lang/rust/issues/TBD

------------------------

To call a closure on each element of an iterator, you can use `for_each`:

```rust
#![feature(iterator_for_each)]

fn main() {
(0..10).for_each(|i| println!("{}", i));
}
```
47 changes: 47 additions & 0 deletions src/libcore/benches/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,50 @@ fn bench_zip_add(b: &mut Bencher) {
add_zip(&source, &mut dst)
});
}

/// `Iterator::for_each` implemented as a plain loop.
fn for_each_loop<I, F>(iter: I, mut f: F) where
I: Iterator, F: FnMut(I::Item)
{
for item in iter {
f(item);
}
}

/// `Iterator::for_each` implemented with `fold` for internal iteration.
/// (except when `by_ref()` effectively disables that optimization.)
fn for_each_fold<I, F>(iter: I, mut f: F) where
I: Iterator, F: FnMut(I::Item)
{
iter.fold((), move |(), item| f(item));
}

#[bench]
fn bench_for_each_chain_loop(b: &mut Bencher) {
b.iter(|| {
let mut acc = 0;
let iter = (0i64..1000000).chain(0..1000000).map(black_box);
for_each_loop(iter, |x| acc += x);
acc
});
}

#[bench]
fn bench_for_each_chain_fold(b: &mut Bencher) {
b.iter(|| {
let mut acc = 0;
let iter = (0i64..1000000).chain(0..1000000).map(black_box);
for_each_fold(iter, |x| acc += x);
acc
});
}

#[bench]
fn bench_for_each_chain_ref_fold(b: &mut Bencher) {
b.iter(|| {
let mut acc = 0;
let mut iter = (0i64..1000000).chain(0..1000000).map(black_box);
for_each_fold(iter.by_ref(), |x| acc += x);
acc
});
}
47 changes: 47 additions & 0 deletions src/libcore/iter/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,53 @@ pub trait Iterator {
Map{iter: self, f: f}
}

/// Calls a closure on each element of an iterator.
///
/// This is equivalent to using a [`for`] loop on the iterator, although
/// `break` and `continue` are not possible from a closure. It's generally
/// more idiomatic to use a `for` loop, but `for_each` may be more legible
/// when processing items at the end of longer iterator chains. In some
/// cases `for_each` may also be faster than a loop, because it will use
/// internal iteration on adaptors like `Chain`.
///
/// [`for`]: ../../book/first-edition/loops.html#for
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(iterator_for_each)]
///
/// use std::sync::mpsc::channel;
///
/// let (tx, rx) = channel();
/// (0..5).map(|x| x * 2 + 1)
/// .for_each(move |x| tx.send(x).unwrap());
///
/// let v: Vec<_> = rx.iter().collect();
/// assert_eq!(v, vec![1, 3, 5, 7, 9]);
/// ```
///
/// For such a small example, a `for` loop may be cleaner, but `for_each`
/// might be preferable to keep a functional style with longer iterators:
///
/// ```
/// #![feature(iterator_for_each)]
///
/// (0..5).flat_map(|x| x * 100 .. x * 110)
/// .enumerate()
/// .filter(|&(i, x)| (i + x) % 3 == 0)
/// .for_each(|(i, x)| println!("{}:{}", i, x));
/// ```
#[inline]
#[unstable(feature = "iterator_for_each", issue = "0")]
fn for_each<F>(self, mut f: F) where
Self: Sized, F: FnMut(Self::Item),
{
self.fold((), move |(), item| f(item));
}

/// Creates an iterator which uses a closure to determine if an element
/// should be yielded.
///
Expand Down

0 comments on commit 919c4a6

Please sign in to comment.