about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-05-25 11:34:43 +0000
committerbors <bors@rust-lang.org>2025-05-25 11:34:43 +0000
commit88b3b520e852e01970c3f519339ba64ed3e7db6d (patch)
treeb17317f4d7bdd5d77927916a7827669e0fb651df
parentaa57e46e24a4a08cc336325e92567b40b0c2ba62 (diff)
parent5240cd3a8210ab56fe5710048f7ab719cbba16a2 (diff)
Auto merge of #141086 - a1phyr:spec_advance_by, r=jhpratt
Implement `advance_by` via `try_fold` for `Sized` iterators

When `try_fold` is overriden, it is usually easier for compilers to optimize.

Example difference: https://iter.godbolt.org/z/z8cEfnKro
-rw-r--r--library/core/src/iter/traits/iterator.rs36
1 files changed, 31 insertions, 5 deletions
diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs
index cf85bdb1352..f296792b1dc 100644
--- a/library/core/src/iter/traits/iterator.rs
+++ b/library/core/src/iter/traits/iterator.rs
@@ -294,13 +294,39 @@ pub trait Iterator {
     #[inline]
     #[unstable(feature = "iter_advance_by", reason = "recently added", issue = "77404")]
     fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
-        for i in 0..n {
-            if self.next().is_none() {
-                // SAFETY: `i` is always less than `n`.
-                return Err(unsafe { NonZero::new_unchecked(n - i) });
+        /// Helper trait to specialize `advance_by` via `try_fold` for `Sized` iterators.
+        trait SpecAdvanceBy {
+            fn spec_advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>>;
+        }
+
+        impl<I: Iterator + ?Sized> SpecAdvanceBy for I {
+            default fn spec_advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
+                for i in 0..n {
+                    if self.next().is_none() {
+                        // SAFETY: `i` is always less than `n`.
+                        return Err(unsafe { NonZero::new_unchecked(n - i) });
+                    }
+                }
+                Ok(())
+            }
+        }
+
+        impl<I: Iterator> SpecAdvanceBy for I {
+            fn spec_advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
+                let Some(n) = NonZero::new(n) else {
+                    return Ok(());
+                };
+
+                let res = self.try_fold(n, |n, _| NonZero::new(n.get() - 1));
+
+                match res {
+                    None => Ok(()),
+                    Some(n) => Err(n),
+                }
             }
         }
-        Ok(())
+
+        self.spec_advance_by(n)
     }
 
     /// Returns the `n`th element of the iterator.