Skip to content

Commit 4b7c3d8

Browse files
committed
Make fold standalone.
`fold` is currently implemented via `try_fold`, but implementing it directly results in slightly less LLVM IR being generated, speeding up compilation of some benchmarks. (And likewise for `rfold`.) The commit adds `fold` implementations to all the iterators that lack one but do have a `try_fold` implementation. Most of these just call the `try_fold` implementation directly.
1 parent 34cce58 commit 4b7c3d8

File tree

6 files changed

+124
-37
lines changed

6 files changed

+124
-37
lines changed

src/libcore/iter/adapters/mod.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,9 @@ where
512512
acc = self.iter.try_fold(acc, &mut f)?;
513513
}
514514
}
515+
516+
// No `fold` override, because `fold` doesn't make much sense for `Cycle`,
517+
// and we can't do anything better than the default.
515518
}
516519

517520
#[stable(feature = "fused", since = "1.26.0")]
@@ -643,6 +646,25 @@ where
643646
}
644647
from_fn(nth(&mut self.iter, self.step)).try_fold(acc, f)
645648
}
649+
650+
fn fold<Acc, F>(mut self, mut acc: Acc, mut f: F) -> Acc
651+
where
652+
F: FnMut(Acc, Self::Item) -> Acc,
653+
{
654+
#[inline]
655+
fn nth<I: Iterator>(iter: &mut I, step: usize) -> impl FnMut() -> Option<I::Item> + '_ {
656+
move || iter.nth(step)
657+
}
658+
659+
if self.first_take {
660+
self.first_take = false;
661+
match self.iter.next() {
662+
None => return acc,
663+
Some(x) => acc = f(acc, x),
664+
}
665+
}
666+
from_fn(nth(&mut self.iter, self.step)).fold(acc, f)
667+
}
646668
}
647669

648670
impl<I> StepBy<I>
@@ -1767,6 +1789,20 @@ where
17671789
self.iter.try_fold(init, check(flag, p, fold)).into_try()
17681790
}
17691791
}
1792+
1793+
#[inline]
1794+
fn fold<Acc, Fold>(mut self, init: Acc, fold: Fold) -> Acc
1795+
where
1796+
Self: Sized,
1797+
Fold: FnMut(Acc, Self::Item) -> Acc,
1798+
{
1799+
#[inline]
1800+
fn ok<B, T>(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result<B, !> {
1801+
move |acc, x| Ok(f(acc, x))
1802+
}
1803+
1804+
self.try_fold(init, ok(fold)).unwrap()
1805+
}
17701806
}
17711807

17721808
#[stable(feature = "fused", since = "1.26.0")]
@@ -1838,6 +1874,20 @@ where
18381874
})
18391875
.into_try()
18401876
}
1877+
1878+
#[inline]
1879+
fn fold<Acc, Fold>(mut self, init: Acc, fold: Fold) -> Acc
1880+
where
1881+
Self: Sized,
1882+
Fold: FnMut(Acc, Self::Item) -> Acc,
1883+
{
1884+
#[inline]
1885+
fn ok<B, T>(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result<B, !> {
1886+
move |acc, x| Ok(f(acc, x))
1887+
}
1888+
1889+
self.try_fold(init, ok(fold)).unwrap()
1890+
}
18411891
}
18421892

18431893
/// An iterator that skips over `n` elements of `iter`.
@@ -2105,6 +2155,20 @@ where
21052155
self.iter.try_fold(init, check(n, fold)).into_try()
21062156
}
21072157
}
2158+
2159+
#[inline]
2160+
fn fold<Acc, Fold>(mut self, init: Acc, fold: Fold) -> Acc
2161+
where
2162+
Self: Sized,
2163+
Fold: FnMut(Acc, Self::Item) -> Acc,
2164+
{
2165+
#[inline]
2166+
fn ok<B, T>(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result<B, !> {
2167+
move |acc, x| Ok(f(acc, x))
2168+
}
2169+
2170+
self.try_fold(init, ok(fold)).unwrap()
2171+
}
21082172
}
21092173

21102174
#[stable(feature = "double_ended_take_iterator", since = "1.38.0")]
@@ -2237,6 +2301,20 @@ where
22372301
let f = &mut self.f;
22382302
self.iter.try_fold(init, scan(state, f, fold)).into_try()
22392303
}
2304+
2305+
#[inline]
2306+
fn fold<Acc, Fold>(mut self, init: Acc, fold: Fold) -> Acc
2307+
where
2308+
Self: Sized,
2309+
Fold: FnMut(Acc, Self::Item) -> Acc,
2310+
{
2311+
#[inline]
2312+
fn ok<B, T>(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result<B, !> {
2313+
move |acc, x| Ok(f(acc, x))
2314+
}
2315+
2316+
self.try_fold(init, ok(fold)).unwrap()
2317+
}
22402318
}
22412319

22422320
/// An iterator that calls a function with a reference to each element before
@@ -2444,4 +2522,17 @@ where
24442522
})
24452523
.into_try()
24462524
}
2525+
2526+
fn fold<B, F>(mut self, init: B, fold: F) -> B
2527+
where
2528+
Self: Sized,
2529+
F: FnMut(B, Self::Item) -> B,
2530+
{
2531+
#[inline]
2532+
fn ok<B, T>(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result<B, !> {
2533+
move |acc, x| Ok(f(acc, x))
2534+
}
2535+
2536+
self.try_fold(init, ok(fold)).unwrap()
2537+
}
24472538
}

src/libcore/iter/range.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,19 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
658658
Try::from_ok(accum)
659659
}
660660

661+
#[inline]
662+
fn fold<B, F>(mut self, init: B, f: F) -> B
663+
where
664+
Self: Sized,
665+
F: FnMut(B, Self::Item) -> B,
666+
{
667+
#[inline]
668+
fn ok<B, T>(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result<B, !> {
669+
move |acc, x| Ok(f(acc, x))
670+
}
671+
672+
self.try_fold(init, ok(f)).unwrap()
673+
}
661674
#[inline]
662675
fn last(mut self) -> Option<A> {
663676
self.next_back()

src/libcore/iter/traits/double_ended.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -221,17 +221,16 @@ pub trait DoubleEndedIterator: Iterator {
221221
/// ```
222222
#[inline]
223223
#[stable(feature = "iter_rfold", since = "1.27.0")]
224-
fn rfold<B, F>(mut self, accum: B, f: F) -> B
224+
fn rfold<B, F>(mut self, init: B, mut f: F) -> B
225225
where
226226
Self: Sized,
227227
F: FnMut(B, Self::Item) -> B,
228228
{
229-
#[inline]
230-
fn ok<B, T>(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result<B, !> {
231-
move |acc, x| Ok(f(acc, x))
229+
let mut accum = init;
230+
while let Some(x) = self.next_back() {
231+
accum = f(accum, x);
232232
}
233-
234-
self.try_rfold(accum, ok(f)).unwrap()
233+
accum
235234
}
236235

237236
/// Searches for an element of an iterator from the back that satisfies a predicate.

src/libcore/iter/traits/iterator.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1826,7 +1826,7 @@ pub trait Iterator {
18261826
///
18271827
/// # Note to Implementors
18281828
///
1829-
/// Most of the other (forward) methods have default implementations in
1829+
/// Several of the other (forward) methods have default implementations in
18301830
/// terms of this one, so try to implement this explicitly if it can
18311831
/// do something better than the default `for` loop implementation.
18321832
///
@@ -1944,6 +1944,15 @@ pub trait Iterator {
19441944
/// may not terminate for infinite iterators, even on traits for which a
19451945
/// result is determinable in finite time.
19461946
///
1947+
/// # Note to Implementors
1948+
///
1949+
/// Several of the other (forward) methods have default implementations in
1950+
/// terms of this one, so try to implement this explicitly if it can
1951+
/// do something better than the default `for` loop implementation.
1952+
///
1953+
/// In particular, try to have this call `fold()` on the internal parts
1954+
/// from which this iterator is composed.
1955+
///
19471956
/// # Examples
19481957
///
19491958
/// Basic usage:
@@ -1992,17 +2001,16 @@ pub trait Iterator {
19922001
/// ```
19932002
#[inline]
19942003
#[stable(feature = "rust1", since = "1.0.0")]
1995-
fn fold<B, F>(mut self, init: B, f: F) -> B
2004+
fn fold<B, F>(mut self, init: B, mut f: F) -> B
19962005
where
19972006
Self: Sized,
19982007
F: FnMut(B, Self::Item) -> B,
19992008
{
2000-
#[inline]
2001-
fn ok<B, T>(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result<B, !> {
2002-
move |acc, x| Ok(f(acc, x))
2009+
let mut accum = init;
2010+
while let Some(x) = self.next() {
2011+
accum = f(accum, x);
20032012
}
2004-
2005-
self.try_fold(init, ok(f)).unwrap()
2013+
accum
20062014
}
20072015

20082016
/// The same as [`fold()`](#method.fold), but uses the first element in the

src/test/codegen/iter-fold-closure-no-dupes.rs

Lines changed: 0 additions & 14 deletions
This file was deleted.

src/test/codegen/iter-fold-closure-no-iterator.rs

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)