diff options
| author | bors <bors@rust-lang.org> | 2017-06-30 09:15:21 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2017-06-30 09:15:21 +0000 |
| commit | 919c4a6707da3aa2cc9ff63e33057e1e2a90720b (patch) | |
| tree | 632c6d954c24235be3944b55305654e4fd66ce8e | |
| parent | 4c5b437176a65c8686865bd7b8f9bb27c239d37c (diff) | |
| parent | e72ee6e4ad0511aaf533a492382b84dfa712393f (diff) | |
| download | rust-919c4a6707da3aa2cc9ff63e33057e1e2a90720b.tar.gz rust-919c4a6707da3aa2cc9ff63e33057e1e2a90720b.zip | |
Auto merge of #42782 - cuviper:iterator_for_each, r=alexcrichton
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 #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
| -rw-r--r-- | src/doc/unstable-book/src/library-features/iterator-for-each.md | 17 | ||||
| -rw-r--r-- | src/libcore/benches/iter.rs | 47 | ||||
| -rw-r--r-- | src/libcore/iter/iterator.rs | 47 |
3 files changed, 111 insertions, 0 deletions
diff --git a/src/doc/unstable-book/src/library-features/iterator-for-each.md b/src/doc/unstable-book/src/library-features/iterator-for-each.md new file mode 100644 index 00000000000..72fdac5e40d --- /dev/null +++ b/src/doc/unstable-book/src/library-features/iterator-for-each.md @@ -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)); +} +``` diff --git a/src/libcore/benches/iter.rs b/src/libcore/benches/iter.rs index 93d38a5bc83..5b06229c21f 100644 --- a/src/libcore/benches/iter.rs +++ b/src/libcore/benches/iter.rs @@ -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 + }); +} diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index 30d09e5453b..26660cb3331 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -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. /// |
