From b4038977a39f7c5bfa76cccf586930ec57befbad Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 20 Jun 2017 15:25:51 -0700 Subject: [PATCH 1/3] 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 --- .../src/library-features/iterator-for-each.md | 17 +++++++ src/libcore/iter/iterator.rs | 46 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 src/doc/unstable-book/src/library-features/iterator-for-each.md 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 0000000000000..72fdac5e40dfa --- /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/iter/iterator.rs b/src/libcore/iter/iterator.rs index 30d09e5453b37..49c43d133e513 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -482,6 +482,52 @@ 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. + /// + /// [`for`]: ../../book/first-edition/loops.html#for + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iterator_for_each)] + /// + /// let mut v = vec![]; + /// (0..5).for_each(|x| v.push(x * 100)); + /// + /// let mut v2 = vec![]; + /// for x in 0..5 { v2.push(x * 100); } + /// + /// assert_eq!(v, v2); + /// ``` + /// + /// For such a small example, the `for` loop is 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(self, mut f: F) where + Self: Sized, F: FnMut(Self::Item), + { + for item in self { + f(item); + } + } + /// Creates an iterator which uses a closure to determine if an element /// should be yielded. /// From 4a8ddac99e1edfb219e11c3ea2d6c43ccecb29ab Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 21 Jun 2017 13:22:27 -0700 Subject: [PATCH 2/3] Use `fold` to implement `Iterator::for_each` The benefit of using internal iteration is shown in new benchmarks: test iter::bench_for_each_chain_fold ... bench: 635,110 ns/iter (+/- 5,135) test iter::bench_for_each_chain_loop ... bench: 2,249,983 ns/iter (+/- 42,001) test iter::bench_for_each_chain_ref_fold ... bench: 2,248,061 ns/iter (+/- 51,940) --- src/libcore/benches/iter.rs | 47 ++++++++++++++++++++++++++++++++++++ src/libcore/iter/iterator.rs | 8 +++--- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/libcore/benches/iter.rs b/src/libcore/benches/iter.rs index 93d38a5bc83bb..5b06229c21f23 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(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(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 49c43d133e513..d38864f3edddb 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -487,7 +487,9 @@ pub trait 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. + /// 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 /// @@ -523,9 +525,7 @@ pub trait Iterator { fn for_each(self, mut f: F) where Self: Sized, F: FnMut(Self::Item), { - for item in self { - f(item); - } + self.fold((), move |(), item| f(item)); } /// Creates an iterator which uses a closure to determine if an element From e72ee6e4ad0511aaf533a492382b84dfa712393f Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 27 Jun 2017 16:31:31 -0700 Subject: [PATCH 3/3] Use a little more compelling example of `for_each` --- src/libcore/iter/iterator.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index d38864f3edddb..26660cb3331ab 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -500,16 +500,17 @@ pub trait Iterator { /// ``` /// #![feature(iterator_for_each)] /// - /// let mut v = vec![]; - /// (0..5).for_each(|x| v.push(x * 100)); + /// use std::sync::mpsc::channel; /// - /// let mut v2 = vec![]; - /// for x in 0..5 { v2.push(x * 100); } + /// let (tx, rx) = channel(); + /// (0..5).map(|x| x * 2 + 1) + /// .for_each(move |x| tx.send(x).unwrap()); /// - /// assert_eq!(v, v2); + /// let v: Vec<_> = rx.iter().collect(); + /// assert_eq!(v, vec![1, 3, 5, 7, 9]); /// ``` /// - /// For such a small example, the `for` loop is cleaner, but `for_each` + /// 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: /// /// ```