]> git.lizzy.rs Git - rust.git/commitdiff
Use `fold` to implement `Iterator::for_each`
authorJosh Stone <jistone@redhat.com>
Wed, 21 Jun 2017 20:22:27 +0000 (13:22 -0700)
committerJosh Stone <jistone@redhat.com>
Wed, 21 Jun 2017 20:22:27 +0000 (13:22 -0700)
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
src/libcore/iter/iterator.rs

index 93d38a5bc83bbf1e392585821c90b82d5a72d3fb..5b06229c21f23367f0682273c1926884659afe12 100644 (file)
@@ -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
+    });
+}
index 49c43d133e51360bc6b4dd8ef04134c0df555d44..d38864f3edddb4b7f28382d9cbdbc944c0350e8b 100644 (file)
@@ -487,7 +487,9 @@ fn map<B, F>(self, f: F) -> Map<Self, F> where
     /// 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 @@ fn map<B, F>(self, f: F) -> Map<Self, F> where
     fn for_each<F>(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