Skip to content

Commit 919c4a6

Browse files
committed
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
2 parents 4c5b437 + e72ee6e commit 919c4a6

File tree

3 files changed

+111
-0
lines changed

3 files changed

+111
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# `iterator_for_each`
2+
3+
The tracking issue for this feature is: [#TBD]
4+
5+
[#TBD]: https://github.com/rust-lang/rust/issues/TBD
6+
7+
------------------------
8+
9+
To call a closure on each element of an iterator, you can use `for_each`:
10+
11+
```rust
12+
#![feature(iterator_for_each)]
13+
14+
fn main() {
15+
(0..10).for_each(|i| println!("{}", i));
16+
}
17+
```

src/libcore/benches/iter.rs

+47
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,50 @@ fn bench_zip_add(b: &mut Bencher) {
9999
add_zip(&source, &mut dst)
100100
});
101101
}
102+
103+
/// `Iterator::for_each` implemented as a plain loop.
104+
fn for_each_loop<I, F>(iter: I, mut f: F) where
105+
I: Iterator, F: FnMut(I::Item)
106+
{
107+
for item in iter {
108+
f(item);
109+
}
110+
}
111+
112+
/// `Iterator::for_each` implemented with `fold` for internal iteration.
113+
/// (except when `by_ref()` effectively disables that optimization.)
114+
fn for_each_fold<I, F>(iter: I, mut f: F) where
115+
I: Iterator, F: FnMut(I::Item)
116+
{
117+
iter.fold((), move |(), item| f(item));
118+
}
119+
120+
#[bench]
121+
fn bench_for_each_chain_loop(b: &mut Bencher) {
122+
b.iter(|| {
123+
let mut acc = 0;
124+
let iter = (0i64..1000000).chain(0..1000000).map(black_box);
125+
for_each_loop(iter, |x| acc += x);
126+
acc
127+
});
128+
}
129+
130+
#[bench]
131+
fn bench_for_each_chain_fold(b: &mut Bencher) {
132+
b.iter(|| {
133+
let mut acc = 0;
134+
let iter = (0i64..1000000).chain(0..1000000).map(black_box);
135+
for_each_fold(iter, |x| acc += x);
136+
acc
137+
});
138+
}
139+
140+
#[bench]
141+
fn bench_for_each_chain_ref_fold(b: &mut Bencher) {
142+
b.iter(|| {
143+
let mut acc = 0;
144+
let mut iter = (0i64..1000000).chain(0..1000000).map(black_box);
145+
for_each_fold(iter.by_ref(), |x| acc += x);
146+
acc
147+
});
148+
}

src/libcore/iter/iterator.rs

+47
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,53 @@ pub trait Iterator {
482482
Map{iter: self, f: f}
483483
}
484484

485+
/// Calls a closure on each element of an iterator.
486+
///
487+
/// This is equivalent to using a [`for`] loop on the iterator, although
488+
/// `break` and `continue` are not possible from a closure. It's generally
489+
/// more idiomatic to use a `for` loop, but `for_each` may be more legible
490+
/// when processing items at the end of longer iterator chains. In some
491+
/// cases `for_each` may also be faster than a loop, because it will use
492+
/// internal iteration on adaptors like `Chain`.
493+
///
494+
/// [`for`]: ../../book/first-edition/loops.html#for
495+
///
496+
/// # Examples
497+
///
498+
/// Basic usage:
499+
///
500+
/// ```
501+
/// #![feature(iterator_for_each)]
502+
///
503+
/// use std::sync::mpsc::channel;
504+
///
505+
/// let (tx, rx) = channel();
506+
/// (0..5).map(|x| x * 2 + 1)
507+
/// .for_each(move |x| tx.send(x).unwrap());
508+
///
509+
/// let v: Vec<_> = rx.iter().collect();
510+
/// assert_eq!(v, vec![1, 3, 5, 7, 9]);
511+
/// ```
512+
///
513+
/// For such a small example, a `for` loop may be cleaner, but `for_each`
514+
/// might be preferable to keep a functional style with longer iterators:
515+
///
516+
/// ```
517+
/// #![feature(iterator_for_each)]
518+
///
519+
/// (0..5).flat_map(|x| x * 100 .. x * 110)
520+
/// .enumerate()
521+
/// .filter(|&(i, x)| (i + x) % 3 == 0)
522+
/// .for_each(|(i, x)| println!("{}:{}", i, x));
523+
/// ```
524+
#[inline]
525+
#[unstable(feature = "iterator_for_each", issue = "0")]
526+
fn for_each<F>(self, mut f: F) where
527+
Self: Sized, F: FnMut(Self::Item),
528+
{
529+
self.fold((), move |(), item| f(item));
530+
}
531+
485532
/// Creates an iterator which uses a closure to determine if an element
486533
/// should be yielded.
487534
///

0 commit comments

Comments
 (0)