about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-06-30 09:15:21 +0000
committerbors <bors@rust-lang.org>2017-06-30 09:15:21 +0000
commit919c4a6707da3aa2cc9ff63e33057e1e2a90720b (patch)
tree632c6d954c24235be3944b55305654e4fd66ce8e
parent4c5b437176a65c8686865bd7b8f9bb27c239d37c (diff)
parente72ee6e4ad0511aaf533a492382b84dfa712393f (diff)
downloadrust-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.md17
-rw-r--r--src/libcore/benches/iter.rs47
-rw-r--r--src/libcore/iter/iterator.rs47
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.
     ///