Skip to content

Commit 7e98ec5

Browse files
authored
Rollup merge of #64121 - timvermeulen:iter_step_by_internal, r=scottmcm
Override `StepBy::{try_fold, try_rfold}` Previous PR: #51435 The previous PR was closed in favor of #51601, which was later reverted. I don't think these implementations will make it harder to specialize `StepBy<Range<_>>` later, so we should be able to land this without any consequences. This should fix #57517 – in my benchmarks `iter` and `iter.step_by(1)` now perform equally well, provided internal iteration is used.
2 parents 457a23f + 78908f2 commit 7e98ec5

File tree

2 files changed

+78
-1
lines changed

2 files changed

+78
-1
lines changed

src/libcore/iter/adapters/mod.rs

+43-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::usize;
55
use crate::intrinsics;
66

77
use super::{Iterator, DoubleEndedIterator, ExactSizeIterator, FusedIterator, TrustedLen};
8-
use super::LoopState;
8+
use super::{LoopState, from_fn};
99

1010
mod chain;
1111
mod flatten;
@@ -534,6 +534,26 @@ impl<I> Iterator for StepBy<I> where I: Iterator {
534534
self.iter.nth(nth - 1);
535535
}
536536
}
537+
538+
fn try_fold<Acc, F, R>(&mut self, mut acc: Acc, mut f: F) -> R
539+
where
540+
F: FnMut(Acc, Self::Item) -> R,
541+
R: Try<Ok = Acc>,
542+
{
543+
#[inline]
544+
fn nth<I: Iterator>(iter: &mut I, step: usize) -> impl FnMut() -> Option<I::Item> + '_ {
545+
move || iter.nth(step)
546+
}
547+
548+
if self.first_take {
549+
self.first_take = false;
550+
match self.iter.next() {
551+
None => return Try::from_ok(acc),
552+
Some(x) => acc = f(acc, x)?,
553+
}
554+
}
555+
from_fn(nth(&mut self.iter, self.step)).try_fold(acc, f)
556+
}
537557
}
538558

539559
impl<I> StepBy<I> where I: ExactSizeIterator {
@@ -567,6 +587,28 @@ impl<I> DoubleEndedIterator for StepBy<I> where I: DoubleEndedIterator + ExactSi
567587
.saturating_add(self.next_back_index());
568588
self.iter.nth_back(n)
569589
}
590+
591+
fn try_rfold<Acc, F, R>(&mut self, init: Acc, mut f: F) -> R
592+
where
593+
F: FnMut(Acc, Self::Item) -> R,
594+
R: Try<Ok = Acc>,
595+
{
596+
#[inline]
597+
fn nth_back<I: DoubleEndedIterator>(
598+
iter: &mut I,
599+
step: usize,
600+
) -> impl FnMut() -> Option<I::Item> + '_ {
601+
move || iter.nth_back(step)
602+
}
603+
604+
match self.next_back() {
605+
None => Try::from_ok(init),
606+
Some(x) => {
607+
let acc = f(init, x)?;
608+
from_fn(nth_back(&mut self.iter, self.step)).try_fold(acc, f)
609+
}
610+
}
611+
}
570612
}
571613

572614
// StepBy can only make the iterator shorter, so the len will still fit.

src/libcore/tests/iter.rs

+35
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,23 @@ fn test_iterator_step_by_nth_overflow() {
385385
assert_eq!(it.0, (usize::MAX as Bigger) * 1);
386386
}
387387

388+
#[test]
389+
fn test_iterator_step_by_nth_try_fold() {
390+
let mut it = (0..).step_by(10);
391+
assert_eq!(it.try_fold(0, i8::checked_add), None);
392+
assert_eq!(it.next(), Some(60));
393+
assert_eq!(it.try_fold(0, i8::checked_add), None);
394+
assert_eq!(it.next(), Some(90));
395+
396+
let mut it = (100..).step_by(10);
397+
assert_eq!(it.try_fold(50, i8::checked_add), None);
398+
assert_eq!(it.next(), Some(110));
399+
400+
let mut it = (100..=100).step_by(10);
401+
assert_eq!(it.next(), Some(100));
402+
assert_eq!(it.try_fold(0, i8::checked_add), Some(0));
403+
}
404+
388405
#[test]
389406
fn test_iterator_step_by_nth_back() {
390407
let mut it = (0..16).step_by(5);
@@ -410,6 +427,24 @@ fn test_iterator_step_by_nth_back() {
410427
assert_eq!(it().nth_back(42), None);
411428
}
412429

430+
#[test]
431+
fn test_iterator_step_by_nth_try_rfold() {
432+
let mut it = (0..100).step_by(10);
433+
assert_eq!(it.try_rfold(0, i8::checked_add), None);
434+
assert_eq!(it.next_back(), Some(70));
435+
assert_eq!(it.next(), Some(0));
436+
assert_eq!(it.try_rfold(0, i8::checked_add), None);
437+
assert_eq!(it.next_back(), Some(30));
438+
439+
let mut it = (0..100).step_by(10);
440+
assert_eq!(it.try_rfold(50, i8::checked_add), None);
441+
assert_eq!(it.next_back(), Some(80));
442+
443+
let mut it = (100..=100).step_by(10);
444+
assert_eq!(it.next_back(), Some(100));
445+
assert_eq!(it.try_fold(0, i8::checked_add), Some(0));
446+
}
447+
413448
#[test]
414449
#[should_panic]
415450
fn test_iterator_step_by_zero() {

0 commit comments

Comments
 (0)