Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f08d5c0

Browse files
committedMar 23, 2025
Auto merge of rust-lang#138833 - joboet:optimize-repeat-n, r=thomcc
core: optimize `RepeatN` ...by adding an optimized implementation of `try_fold` and `fold` as well as replacing some unnecessary `mem::replace` calls with `MaybeUninit` helper methods.
2 parents 756bff9 + 51d51c8 commit f08d5c0

File tree

1 file changed

+55
-4
lines changed

1 file changed

+55
-4
lines changed
 

‎library/core/src/iter/sources/repeat_n.rs

+55-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use crate::fmt;
22
use crate::iter::{FusedIterator, TrustedLen, UncheckedIterator};
3-
use crate::mem::{self, MaybeUninit};
3+
use crate::mem::MaybeUninit;
44
use crate::num::NonZero;
5+
use crate::ops::{NeverShortCircuit, Try};
56

67
/// Creates a new iterator that repeats a single element a given number of times.
78
///
@@ -95,10 +96,10 @@ impl<A> RepeatN<A> {
9596
fn take_element(&mut self) -> Option<A> {
9697
if self.count > 0 {
9798
self.count = 0;
98-
let element = mem::replace(&mut self.element, MaybeUninit::uninit());
9999
// SAFETY: We just set count to zero so it won't be dropped again,
100100
// and it used to be non-zero so it hasn't already been dropped.
101-
unsafe { Some(element.assume_init()) }
101+
let element = unsafe { self.element.assume_init_read() };
102+
Some(element)
102103
} else {
103104
None
104105
}
@@ -169,6 +170,39 @@ impl<A: Clone> Iterator for RepeatN<A> {
169170
}
170171
}
171172

173+
fn try_fold<B, F, R>(&mut self, mut acc: B, mut f: F) -> R
174+
where
175+
F: FnMut(B, A) -> R,
176+
R: Try<Output = B>,
177+
{
178+
if self.count > 0 {
179+
while self.count > 1 {
180+
self.count -= 1;
181+
// SAFETY: the count was larger than 1, so the element is
182+
// initialized and hasn't been dropped.
183+
acc = f(acc, unsafe { self.element.assume_init_ref().clone() })?;
184+
}
185+
186+
// We could just set the count to zero directly, but doing it this
187+
// way should make it easier for the optimizer to fold this tail
188+
// into the loop when `clone()` is equivalent to copying.
189+
self.count -= 1;
190+
// SAFETY: we just set the count to zero from one, so the element
191+
// is still initialized, has not been dropped yet and will not be
192+
// accessed by future calls.
193+
f(acc, unsafe { self.element.assume_init_read() })
194+
} else {
195+
try { acc }
196+
}
197+
}
198+
199+
fn fold<B, F>(mut self, init: B, f: F) -> B
200+
where
201+
F: FnMut(B, A) -> B,
202+
{
203+
self.try_fold(init, NeverShortCircuit::wrap_mut_2(f)).0
204+
}
205+
172206
#[inline]
173207
fn last(mut self) -> Option<A> {
174208
self.take_element()
@@ -203,6 +237,23 @@ impl<A: Clone> DoubleEndedIterator for RepeatN<A> {
203237
fn nth_back(&mut self, n: usize) -> Option<A> {
204238
self.nth(n)
205239
}
240+
241+
#[inline]
242+
fn try_rfold<B, F, R>(&mut self, init: B, f: F) -> R
243+
where
244+
F: FnMut(B, A) -> R,
245+
R: Try<Output = B>,
246+
{
247+
self.try_fold(init, f)
248+
}
249+
250+
#[inline]
251+
fn rfold<B, F>(self, init: B, f: F) -> B
252+
where
253+
F: FnMut(B, A) -> B,
254+
{
255+
self.fold(init, f)
256+
}
206257
}
207258

208259
#[stable(feature = "iter_repeat_n", since = "1.82.0")]
@@ -220,7 +271,7 @@ impl<A: Clone> UncheckedIterator for RepeatN<A> {
220271
// SAFETY: the check above ensured that the count used to be non-zero,
221272
// so element hasn't been dropped yet, and we just lowered the count to
222273
// zero so it won't be dropped later, and thus it's okay to take it here.
223-
unsafe { mem::replace(&mut self.element, MaybeUninit::uninit()).assume_init() }
274+
unsafe { self.element.assume_init_read() }
224275
} else {
225276
// SAFETY: the count is non-zero, so it must have not been dropped yet.
226277
let element = unsafe { self.element.assume_init_ref() };

0 commit comments

Comments
 (0)
Failed to load comments.