about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-02-25 00:36:05 +0000
committerbors <bors@rust-lang.org>2021-02-25 00:36:05 +0000
commit63bacf14cd06f67171546670f22d8af509c29027 (patch)
treec2ad0b6a143522c477adfa081c37174116838675
parent1fdadbf13aecd190b277ea2aa1b125d2ed986d55 (diff)
parentfc150d17b5e705132f0f70595dfc213aa6b564d3 (diff)
downloadrust-63bacf14cd06f67171546670f22d8af509c29027.tar.gz
rust-63bacf14cd06f67171546670f22d8af509c29027.zip
Auto merge of #82162 - cuviper:flat-fold, r=Mark-Simulacrum
Expand FlattenCompat folds

The former `chain`+`chain`+`fold` implementation looked nice from a
functional-programming perspective, but it introduced unnecessary layers
of abstraction on every `flat_map`/`flatten` fold. It's straightforward
to just fold each part in turn, and this makes it look like a simplified
version of the existing `try_fold` implementation.

For the `iter::bench_flat_map*` benchmarks, I get a large improvement in
`bench_flat_map_chain_sum`, from 1,598,473 ns/iter to 499,889 ns/iter,
and the rest are unchanged.
-rw-r--r--library/core/src/iter/adapters/flatten.rs55
1 files changed, 35 insertions, 20 deletions
diff --git a/library/core/src/iter/adapters/flatten.rs b/library/core/src/iter/adapters/flatten.rs
index 081f282edcf..0114d7af4f4 100644
--- a/library/core/src/iter/adapters/flatten.rs
+++ b/library/core/src/iter/adapters/flatten.rs
@@ -325,22 +325,28 @@ where
     }
 
     #[inline]
-    fn fold<Acc, Fold>(self, init: Acc, ref mut fold: Fold) -> Acc
+    fn fold<Acc, Fold>(self, mut init: Acc, mut fold: Fold) -> Acc
     where
         Fold: FnMut(Acc, Self::Item) -> Acc,
     {
         #[inline]
-        fn flatten<U: Iterator, Acc>(
-            fold: &mut impl FnMut(Acc, U::Item) -> Acc,
-        ) -> impl FnMut(Acc, U) -> Acc + '_ {
-            move |acc, iter| iter.fold(acc, &mut *fold)
+        fn flatten<T: IntoIterator, Acc>(
+            fold: &mut impl FnMut(Acc, T::Item) -> Acc,
+        ) -> impl FnMut(Acc, T) -> Acc + '_ {
+            move |acc, x| x.into_iter().fold(acc, &mut *fold)
         }
 
-        self.frontiter
-            .into_iter()
-            .chain(self.iter.map(IntoIterator::into_iter))
-            .chain(self.backiter)
-            .fold(init, flatten(fold))
+        if let Some(front) = self.frontiter {
+            init = front.fold(init, &mut fold);
+        }
+
+        init = self.iter.fold(init, flatten(&mut fold));
+
+        if let Some(back) = self.backiter {
+            init = back.fold(init, &mut fold);
+        }
+
+        init
     }
 }
 
@@ -411,21 +417,30 @@ where
     }
 
     #[inline]
-    fn rfold<Acc, Fold>(self, init: Acc, ref mut fold: Fold) -> Acc
+    fn rfold<Acc, Fold>(self, mut init: Acc, mut fold: Fold) -> Acc
     where
         Fold: FnMut(Acc, Self::Item) -> Acc,
     {
         #[inline]
-        fn flatten<U: DoubleEndedIterator, Acc>(
-            fold: &mut impl FnMut(Acc, U::Item) -> Acc,
-        ) -> impl FnMut(Acc, U) -> Acc + '_ {
-            move |acc, iter| iter.rfold(acc, &mut *fold)
+        fn flatten<T: IntoIterator, Acc>(
+            fold: &mut impl FnMut(Acc, T::Item) -> Acc,
+        ) -> impl FnMut(Acc, T) -> Acc + '_
+        where
+            T::IntoIter: DoubleEndedIterator,
+        {
+            move |acc, x| x.into_iter().rfold(acc, &mut *fold)
+        }
+
+        if let Some(back) = self.backiter {
+            init = back.rfold(init, &mut fold);
+        }
+
+        init = self.iter.rfold(init, flatten(&mut fold));
+
+        if let Some(front) = self.frontiter {
+            init = front.rfold(init, &mut fold);
         }
 
-        self.frontiter
-            .into_iter()
-            .chain(self.iter.map(IntoIterator::into_iter))
-            .chain(self.backiter)
-            .rfold(init, flatten(fold))
+        init
     }
 }