From 13724fafdc62e3e7e75822554cadb15ff5ce3035 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 25 Sep 2017 20:53:08 -0700 Subject: [PATCH] Add more custom folding to `core::iter` adaptors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many of the iterator adaptors will perform faster folds if they forward to their inner iterator's folds, especially for inner types like `Chain` which are optimized too. The following types are newly specialized: | Type | `fold` | `rfold` | | ----------- | ------ | ------- | | `Enumerate` | ✓ | ✓ | | `Filter` | ✓ | ✓ | | `FilterMap` | ✓ | ✓ | | `FlatMap` | exists | ✓ | | `Fuse` | ✓ | ✓ | | `Inspect` | ✓ | ✓ | | `Peekable` | ✓ | N/A¹ | | `Skip` | ✓ | N/A² | | `SkipWhile` | ✓ | N/A¹ | ¹ not a `DoubleEndedIterator` ² `Skip::next_back` doesn't pull skipped items at all, but this couldn't be avoided if `Skip::rfold` were to call its inner iterator's `rfold`. Benchmarks ---------- In the following results, plain `_sum` computes the sum of a million integers -- note that `sum()` is implemented with `fold()`. The `_ref_sum` variants do the same on a `by_ref()` iterator, which is limited to calling `next()` one by one, without specialized `fold`. The `chain` variants perform the same tests on two iterators chained together, to show a greater benefit of forwarding `fold` internally. test iter::bench_enumerate_chain_ref_sum ... bench: 2,216,264 ns/iter (+/- 29,228) test iter::bench_enumerate_chain_sum ... bench: 922,380 ns/iter (+/- 2,676) test iter::bench_enumerate_ref_sum ... bench: 476,094 ns/iter (+/- 7,110) test iter::bench_enumerate_sum ... bench: 476,438 ns/iter (+/- 3,334) test iter::bench_filter_chain_ref_sum ... bench: 2,266,095 ns/iter (+/- 6,051) test iter::bench_filter_chain_sum ... bench: 745,594 ns/iter (+/- 2,013) test iter::bench_filter_ref_sum ... bench: 889,696 ns/iter (+/- 1,188) test iter::bench_filter_sum ... bench: 667,325 ns/iter (+/- 1,894) test iter::bench_filter_map_chain_ref_sum ... bench: 2,259,195 ns/iter (+/- 353,440) test iter::bench_filter_map_chain_sum ... bench: 1,223,280 ns/iter (+/- 1,972) test iter::bench_filter_map_ref_sum ... bench: 611,607 ns/iter (+/- 2,507) test iter::bench_filter_map_sum ... bench: 611,610 ns/iter (+/- 472) test iter::bench_fuse_chain_ref_sum ... bench: 2,246,106 ns/iter (+/- 22,395) test iter::bench_fuse_chain_sum ... bench: 634,887 ns/iter (+/- 1,341) test iter::bench_fuse_ref_sum ... bench: 444,816 ns/iter (+/- 1,748) test iter::bench_fuse_sum ... bench: 316,954 ns/iter (+/- 2,616) test iter::bench_inspect_chain_ref_sum ... bench: 2,245,431 ns/iter (+/- 21,371) test iter::bench_inspect_chain_sum ... bench: 631,645 ns/iter (+/- 4,928) test iter::bench_inspect_ref_sum ... bench: 317,437 ns/iter (+/- 702) test iter::bench_inspect_sum ... bench: 315,942 ns/iter (+/- 4,320) test iter::bench_peekable_chain_ref_sum ... bench: 2,243,585 ns/iter (+/- 12,186) test iter::bench_peekable_chain_sum ... bench: 634,848 ns/iter (+/- 1,712) test iter::bench_peekable_ref_sum ... bench: 444,808 ns/iter (+/- 480) test iter::bench_peekable_sum ... bench: 317,133 ns/iter (+/- 3,309) test iter::bench_skip_chain_ref_sum ... bench: 1,778,734 ns/iter (+/- 2,198) test iter::bench_skip_chain_sum ... bench: 761,850 ns/iter (+/- 1,645) test iter::bench_skip_ref_sum ... bench: 478,207 ns/iter (+/- 119,252) test iter::bench_skip_sum ... bench: 315,614 ns/iter (+/- 3,054) test iter::bench_skip_while_chain_ref_sum ... bench: 2,486,370 ns/iter (+/- 4,845) test iter::bench_skip_while_chain_sum ... bench: 633,915 ns/iter (+/- 5,892) test iter::bench_skip_while_ref_sum ... bench: 666,926 ns/iter (+/- 804) test iter::bench_skip_while_sum ... bench: 444,405 ns/iter (+/- 571) --- src/libcore/benches/iter.rs | 151 ++++++++++++++++++++++++------ src/libcore/iter/mod.rs | 172 ++++++++++++++++++++++++++++++++++ src/libcore/tests/iter.rs | 180 +++++++++++++++++++++++++++++++++++- src/libcore/tests/lib.rs | 1 + 4 files changed, 471 insertions(+), 33 deletions(-) diff --git a/src/libcore/benches/iter.rs b/src/libcore/benches/iter.rs index 827c6354c60..1f16f5b1df3 100644 --- a/src/libcore/benches/iter.rs +++ b/src/libcore/benches/iter.rs @@ -147,40 +147,131 @@ fn bench_for_each_chain_ref_fold(b: &mut Bencher) { }); } -#[bench] -fn bench_flat_map_sum(b: &mut Bencher) { - b.iter(|| -> i64 { - (0i64..1000).flat_map(|x| x..x+1000) - .map(black_box) - .sum() - }); + +/// Helper to benchmark `sum` for iterators taken by value which +/// can optimize `fold`, and by reference which cannot. +macro_rules! bench_sums { + ($bench_sum:ident, $bench_ref_sum:ident, $iter:expr) => { + #[bench] + fn $bench_sum(b: &mut Bencher) { + b.iter(|| -> i64 { + $iter.map(black_box).sum() + }); + } + + #[bench] + fn $bench_ref_sum(b: &mut Bencher) { + b.iter(|| -> i64 { + $iter.map(black_box).by_ref().sum() + }); + } + } } -#[bench] -fn bench_flat_map_ref_sum(b: &mut Bencher) { - b.iter(|| -> i64 { - (0i64..1000).flat_map(|x| x..x+1000) - .map(black_box) - .by_ref() - .sum() - }); +bench_sums! { + bench_flat_map_sum, + bench_flat_map_ref_sum, + (0i64..1000).flat_map(|x| x..x+1000) } -#[bench] -fn bench_flat_map_chain_sum(b: &mut Bencher) { - b.iter(|| -> i64 { - (0i64..1000000).flat_map(|x| once(x).chain(once(x))) - .map(black_box) - .sum() - }); +bench_sums! { + bench_flat_map_chain_sum, + bench_flat_map_chain_ref_sum, + (0i64..1000000).flat_map(|x| once(x).chain(once(x))) } -#[bench] -fn bench_flat_map_chain_ref_sum(b: &mut Bencher) { - b.iter(|| -> i64 { - (0i64..1000000).flat_map(|x| once(x).chain(once(x))) - .map(black_box) - .by_ref() - .sum() - }); +bench_sums! { + bench_enumerate_sum, + bench_enumerate_ref_sum, + (0i64..1000000).enumerate().map(|(i, x)| x * i as i64) +} + +bench_sums! { + bench_enumerate_chain_sum, + bench_enumerate_chain_ref_sum, + (0i64..1000000).chain(0..1000000).enumerate().map(|(i, x)| x * i as i64) +} + +bench_sums! { + bench_filter_sum, + bench_filter_ref_sum, + (0i64..1000000).filter(|x| x % 2 == 0) +} + +bench_sums! { + bench_filter_chain_sum, + bench_filter_chain_ref_sum, + (0i64..1000000).chain(0..1000000).filter(|x| x % 2 == 0) +} + +bench_sums! { + bench_filter_map_sum, + bench_filter_map_ref_sum, + (0i64..1000000).filter_map(|x| x.checked_mul(x)) +} + +bench_sums! { + bench_filter_map_chain_sum, + bench_filter_map_chain_ref_sum, + (0i64..1000000).chain(0..1000000).filter_map(|x| x.checked_mul(x)) +} + +bench_sums! { + bench_fuse_sum, + bench_fuse_ref_sum, + (0i64..1000000).fuse() +} + +bench_sums! { + bench_fuse_chain_sum, + bench_fuse_chain_ref_sum, + (0i64..1000000).chain(0..1000000).fuse() +} + +bench_sums! { + bench_inspect_sum, + bench_inspect_ref_sum, + (0i64..1000000).inspect(|_| {}) +} + +bench_sums! { + bench_inspect_chain_sum, + bench_inspect_chain_ref_sum, + (0i64..1000000).chain(0..1000000).inspect(|_| {}) +} + +bench_sums! { + bench_peekable_sum, + bench_peekable_ref_sum, + (0i64..1000000).peekable() +} + +bench_sums! { + bench_peekable_chain_sum, + bench_peekable_chain_ref_sum, + (0i64..1000000).chain(0..1000000).peekable() +} + +bench_sums! { + bench_skip_sum, + bench_skip_ref_sum, + (0i64..1000000).skip(1000) +} + +bench_sums! { + bench_skip_chain_sum, + bench_skip_chain_ref_sum, + (0i64..1000000).chain(0..1000000).skip(1000) +} + +bench_sums! { + bench_skip_while_sum, + bench_skip_while_ref_sum, + (0i64..1000000).skip_while(|&x| x < 1000) +} + +bench_sums! { + bench_skip_while_chain_sum, + bench_skip_while_chain_ref_sum, + (0i64..1000000).chain(0..1000000).skip_while(|&x| x < 1000) } diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 7907f2fd661..05a5f63680c 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -1238,6 +1238,18 @@ impl Iterator for Filter where P: FnMut(&I::Item) -> bool } count } + + #[inline] + fn fold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut predicate = self.predicate; + self.iter.fold(init, move |acc, item| if predicate(&item) { + fold(acc, item) + } else { + acc + }) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1253,6 +1265,18 @@ impl DoubleEndedIterator for Filter } None } + + #[inline] + fn rfold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut predicate = self.predicate; + self.iter.rfold(init, move |acc, item| if predicate(&item) { + fold(acc, item) + } else { + acc + }) + } } #[unstable(feature = "fused", issue = "35602")] @@ -1304,6 +1328,17 @@ impl Iterator for FilterMap let (_, upper) = self.iter.size_hint(); (0, upper) // can't know a lower bound, due to the predicate } + + #[inline] + fn fold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.fold(init, move |acc, item| match f(item) { + Some(x) => fold(acc, x), + None => acc, + }) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1319,6 +1354,17 @@ impl DoubleEndedIterator for FilterMap } None } + + #[inline] + fn rfold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.rfold(init, move |acc, item| match f(item) { + Some(x) => fold(acc, x), + None => acc, + }) + } } #[unstable(feature = "fused", issue = "35602")] @@ -1383,6 +1429,19 @@ impl Iterator for Enumerate where I: Iterator { fn count(self) -> usize { self.iter.count() } + + #[inline] + #[rustc_inherit_overflow_checks] + fn fold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut count = self.count; + self.iter.fold(init, move |acc, item| { + let acc = fold(acc, (count, item)); + count += 1; + acc + }) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1398,6 +1457,19 @@ impl DoubleEndedIterator for Enumerate where (self.count + len, a) }) } + + #[inline] + fn rfold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + // Can safely add and subtract the count, as `ExactSizeIterator` promises + // that the number of elements fits into a `usize`. + let mut count = self.count + self.iter.len(); + self.iter.rfold(init, move |acc, item| { + count -= 1; + fold(acc, (count, item)) + }) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1509,6 +1581,18 @@ impl Iterator for Peekable { let hi = hi.and_then(|x| x.checked_add(peek_len)); (lo, hi) } + + #[inline] + fn fold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let acc = match self.peeked { + Some(None) => return init, + Some(Some(v)) => fold(init, v), + None => init, + }; + self.iter.fold(acc, fold) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1617,6 +1701,19 @@ impl Iterator for SkipWhile let (_, upper) = self.iter.size_hint(); (0, upper) // can't know a lower bound, due to the predicate } + + #[inline] + fn fold(mut self, mut init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + if !self.flag { + match self.next() { + Some(v) => init = fold(init, v), + None => return init, + } + } + self.iter.fold(init, fold) + } } #[unstable(feature = "fused", issue = "35602")] @@ -1757,6 +1854,19 @@ impl Iterator for Skip where I: Iterator { (lower, upper) } + + #[inline] + fn fold(mut self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + if self.n > 0 { + // nth(n) skips n+1 + if self.iter.nth(self.n - 1).is_none() { + return init; + } + } + self.iter.fold(init, fold) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1979,6 +2089,16 @@ impl DoubleEndedIterator for FlatMap wher } } } + + #[inline] + fn rfold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.frontiter.into_iter() + .chain(self.iter.map(self.f).map(U::into_iter)) + .chain(self.backiter) + .rfold(init, |acc, iter| iter.rfold(acc, &mut fold)) + } } #[unstable(feature = "fused", issue = "35602")] @@ -2056,6 +2176,17 @@ impl Iterator for Fuse where I: Iterator { self.iter.size_hint() } } + + #[inline] + default fn fold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + if self.done { + init + } else { + self.iter.fold(init, fold) + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -2070,6 +2201,17 @@ impl DoubleEndedIterator for Fuse where I: DoubleEndedIterator { next } } + + #[inline] + default fn rfold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + if self.done { + init + } else { + self.iter.rfold(init, fold) + } + } } unsafe impl TrustedRandomAccess for Fuse @@ -2110,6 +2252,13 @@ impl Iterator for Fuse where I: FusedIterator { fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } + + #[inline] + fn fold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.fold(init, fold) + } } #[unstable(feature = "fused", reason = "recently added", issue = "35602")] @@ -2120,6 +2269,13 @@ impl DoubleEndedIterator for Fuse fn next_back(&mut self) -> Option<::Item> { self.iter.next_back() } + + #[inline] + fn rfold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.rfold(init, fold) + } } @@ -2184,6 +2340,14 @@ impl Iterator for Inspect where F: FnMut(&I::Item) { fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } + + #[inline] + fn fold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.fold(init, move |acc, item| { f(&item); fold(acc, item) }) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -2195,6 +2359,14 @@ impl DoubleEndedIterator for Inspect let next = self.iter.next_back(); self.do_inspect(next) } + + #[inline] + fn rfold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.rfold(init, move |acc, item| { f(&item); fold(acc, item) }) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index 59ae30de452..89454c52432 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -248,6 +248,25 @@ fn test_filter_map() { assert_eq!(it.collect::>(), [0*0, 2*2, 4*4, 6*6, 8*8]); } +#[test] +fn test_filter_map_fold() { + let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let ys = [0*0, 2*2, 4*4, 6*6, 8*8]; + let it = xs.iter().filter_map(|&x| if x % 2 == 0 { Some(x*x) } else { None }); + let i = it.fold(0, |i, x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let it = xs.iter().filter_map(|&x| if x % 2 == 0 { Some(x*x) } else { None }); + let i = it.rfold(ys.len(), |i, x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); +} + #[test] fn test_iterator_enumerate() { let xs = [0, 1, 2, 3, 4, 5]; @@ -282,7 +301,31 @@ fn test_iterator_enumerate_nth() { #[test] fn test_iterator_enumerate_count() { let xs = [0, 1, 2, 3, 4, 5]; - assert_eq!(xs.iter().count(), 6); + assert_eq!(xs.iter().enumerate().count(), 6); +} + +#[test] +fn test_iterator_enumerate_fold() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().enumerate(); + // steal a couple to get an interesting offset + assert_eq!(it.next(), Some((0, &0))); + assert_eq!(it.next(), Some((1, &1))); + let i = it.fold(2, |i, (j, &x)| { + assert_eq!(i, j); + assert_eq!(x, xs[j]); + i + 1 + }); + assert_eq!(i, xs.len()); + + let mut it = xs.iter().enumerate(); + assert_eq!(it.next(), Some((0, &0))); + let i = it.rfold(xs.len() - 1, |i, (j, &x)| { + assert_eq!(i, j); + assert_eq!(x, xs[j]); + i - 1 + }); + assert_eq!(i, 0); } #[test] @@ -291,6 +334,25 @@ fn test_iterator_filter_count() { assert_eq!(xs.iter().filter(|&&x| x % 2 == 0).count(), 5); } +#[test] +fn test_iterator_filter_fold() { + let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let ys = [0, 2, 4, 6, 8]; + let it = xs.iter().filter(|&&x| x % 2 == 0); + let i = it.fold(0, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let it = xs.iter().filter(|&&x| x % 2 == 0); + let i = it.rfold(ys.len(), |i, &x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); +} + #[test] fn test_iterator_peekable() { let xs = vec![0, 1, 2, 3, 4, 5]; @@ -381,6 +443,18 @@ fn test_iterator_peekable_last() { assert_eq!(it.last(), None); } +#[test] +fn test_iterator_peekable_fold() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().peekable(); + assert_eq!(it.peek(), Some(&&0)); + let i = it.fold(0, |i, &x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); +} + /// This is an iterator that follows the Iterator contract, /// but it is not fused. After having returned None once, it will start /// producing elements if .next() is called again. @@ -470,6 +544,26 @@ fn test_iterator_skip_while() { assert_eq!(i, ys.len()); } +#[test] +fn test_iterator_skip_while_fold() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; + let ys = [15, 16, 17, 19]; + let it = xs.iter().skip_while(|&x| *x < 15); + let i = it.fold(0, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let mut it = xs.iter().skip_while(|&x| *x < 15); + assert_eq!(it.next(), Some(&ys[0])); // process skips before folding + let i = it.fold(1, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); +} + #[test] fn test_iterator_skip() { let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; @@ -566,6 +660,26 @@ fn test_iterator_skip_last() { assert_eq!(it.last(), Some(&30)); } +#[test] +fn test_iterator_skip_fold() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; + let ys = [13, 15, 16, 17, 19, 20, 30]; + let it = xs.iter().skip(5); + let i = it.fold(0, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let mut it = xs.iter().skip(5); + assert_eq!(it.next(), Some(&ys[0])); // process skips before folding + let i = it.fold(1, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); +} + #[test] fn test_iterator_take() { let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; @@ -661,13 +775,22 @@ fn test_iterator_flat_map_fold() { let xs = [0, 3, 6]; let ys = [1, 2, 3, 4, 5, 6, 7]; let mut it = xs.iter().flat_map(|&x| x..x+3); - it.next(); - it.next_back(); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); let i = it.fold(0, |i, x| { assert_eq!(x, ys[i]); i + 1 }); assert_eq!(i, ys.len()); + + let mut it = xs.iter().flat_map(|&x| x..x+3); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); + let i = it.rfold(ys.len(), |i, x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); } #[test] @@ -684,6 +807,32 @@ fn test_inspect() { assert_eq!(&xs[..], &ys[..]); } +#[test] +fn test_inspect_fold() { + let xs = [1, 2, 3, 4]; + let mut n = 0; + { + let it = xs.iter().inspect(|_| n += 1); + let i = it.fold(0, |i, &x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); + } + assert_eq!(n, xs.len()); + + let mut n = 0; + { + let it = xs.iter().inspect(|_| n += 1); + let i = it.rfold(xs.len(), |i, &x| { + assert_eq!(x, xs[i - 1]); + i - 1 + }); + assert_eq!(i, 0); + } + assert_eq!(n, xs.len()); +} + #[test] fn test_cycle() { let cycle_len = 3; @@ -1241,6 +1390,31 @@ fn test_fuse_count() { // Can't check len now because count consumes. } +#[test] +fn test_fuse_fold() { + let xs = [0, 1, 2]; + let it = xs.iter(); // `FusedIterator` + let i = it.fuse().fold(0, |i, &x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); + + let it = xs.iter(); // `FusedIterator` + let i = it.fuse().rfold(xs.len(), |i, &x| { + assert_eq!(x, xs[i - 1]); + i - 1 + }); + assert_eq!(i, 0); + + let it = xs.iter().scan((), |_, &x| Some(x)); // `!FusedIterator` + let i = it.fuse().fold(0, |i, x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); +} + #[test] fn test_once() { let mut it = once(42); diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 47995597a0a..d928276ad2f 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -25,6 +25,7 @@ #![feature(inclusive_range)] #![feature(inclusive_range_syntax)] #![feature(iter_rfind)] +#![feature(iter_rfold)] #![feature(nonzero)] #![feature(rand)] #![feature(raw)]