Skip to content

Commit 477db05

Browse files
authored
Rollup merge of #62737 - timvermeulen:cycle_try_fold, r=scottmcm
Override Cycle::try_fold It's not very pretty, but I believe this is the simplest way to correctly implement `Cycle::try_fold`. The following may seem correct: ```rust loop { acc = self.iter.try_fold(acc, &mut f)?; self.iter = self.orig.clone(); } ``` ...but this loops infinitely in case `self.orig` is empty, as opposed to returning `acc`. So we first have to fully iterate `self.orig` to check whether it is empty or not, and before _that_, we have to iterate the remaining elements of `self.iter`. This should always call `self.orig.clone()` the same amount of times as repeated `next()` calls would. r? @scottmcm
2 parents e910be8 + 688c112 commit 477db05

File tree

2 files changed

+42
-0
lines changed

2 files changed

+42
-0
lines changed

src/libcore/iter/adapters/mod.rs

+30
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,36 @@ impl<I> Iterator for Cycle<I> where I: Clone + Iterator {
405405
_ => (usize::MAX, None)
406406
}
407407
}
408+
409+
#[inline]
410+
fn try_fold<Acc, F, R>(&mut self, mut acc: Acc, mut f: F) -> R
411+
where
412+
F: FnMut(Acc, Self::Item) -> R,
413+
R: Try<Ok = Acc>,
414+
{
415+
// fully iterate the current iterator. this is necessary because
416+
// `self.iter` may be empty even when `self.orig` isn't
417+
acc = self.iter.try_fold(acc, &mut f)?;
418+
self.iter = self.orig.clone();
419+
420+
// complete a full cycle, keeping track of whether the cycled
421+
// iterator is empty or not. we need to return early in case
422+
// of an empty iterator to prevent an infinite loop
423+
let mut is_empty = true;
424+
acc = self.iter.try_fold(acc, |acc, x| {
425+
is_empty = false;
426+
f(acc, x)
427+
})?;
428+
429+
if is_empty {
430+
return Try::from_ok(acc);
431+
}
432+
433+
loop {
434+
self.iter = self.orig.clone();
435+
acc = self.iter.try_fold(acc, &mut f)?;
436+
}
437+
}
408438
}
409439

410440
#[stable(feature = "fused", since = "1.26.0")]

src/libcore/tests/iter.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,18 @@ fn test_cycle() {
11521152
assert_eq!(empty::<i32>().cycle().fold(0, |acc, x| acc + x), 0);
11531153

11541154
assert_eq!(once(1).cycle().skip(1).take(4).fold(0, |acc, x| acc + x), 4);
1155+
1156+
assert_eq!((0..10).cycle().take(5).sum::<i32>(), 10);
1157+
assert_eq!((0..10).cycle().take(15).sum::<i32>(), 55);
1158+
assert_eq!((0..10).cycle().take(25).sum::<i32>(), 100);
1159+
1160+
let mut iter = (0..10).cycle();
1161+
iter.nth(14);
1162+
assert_eq!(iter.take(8).sum::<i32>(), 38);
1163+
1164+
let mut iter = (0..10).cycle();
1165+
iter.nth(9);
1166+
assert_eq!(iter.take(3).sum::<i32>(), 3);
11551167
}
11561168

11571169
#[test]

0 commit comments

Comments
 (0)