Skip to content

Commit 1b1e887

Browse files
committed
Override try_[r]fold for RangeInclusive
Because the last item needs special handling, it seems that LLVM has trouble canonicalizing the loops in external iteration. With the override, it becomes obvious that the start==end case exits the loop (as opposed to the one *after* that exiting the loop in external iteration).
1 parent b8f2674 commit 1b1e887

File tree

2 files changed

+65
-1
lines changed

2 files changed

+65
-1
lines changed

src/libcore/iter/range.rs

+45-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use convert::TryFrom;
1212
use mem;
13-
use ops::{self, Add, Sub};
13+
use ops::{self, Add, Sub, Try};
1414
use usize;
1515

1616
use super::{FusedIterator, TrustedLen};
@@ -397,6 +397,28 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
397397
fn max(mut self) -> Option<A> {
398398
self.next_back()
399399
}
400+
401+
#[inline]
402+
fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R where
403+
Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Ok=B>
404+
{
405+
let mut accum = init;
406+
if self.start <= self.end {
407+
loop {
408+
let (x, done) =
409+
if self.start < self.end {
410+
let n = self.start.add_one();
411+
(mem::replace(&mut self.start, n), false)
412+
} else {
413+
self.end.replace_zero();
414+
(self.start.replace_one(), true)
415+
};
416+
accum = f(accum, x)?;
417+
if done { break }
418+
}
419+
}
420+
Try::from_ok(accum)
421+
}
400422
}
401423

402424
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
@@ -418,6 +440,28 @@ impl<A: Step> DoubleEndedIterator for ops::RangeInclusive<A> {
418440
_ => None,
419441
}
420442
}
443+
444+
#[inline]
445+
fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R where
446+
Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Ok=B>
447+
{
448+
let mut accum = init;
449+
if self.start <= self.end {
450+
loop {
451+
let (x, done) =
452+
if self.start < self.end {
453+
let n = self.end.sub_one();
454+
(mem::replace(&mut self.end, n), false)
455+
} else {
456+
self.start.replace_one();
457+
(self.end.replace_zero(), true)
458+
};
459+
accum = f(accum, x)?;
460+
if done { break }
461+
}
462+
}
463+
Try::from_ok(accum)
464+
}
421465
}
422466

423467
#[unstable(feature = "fused", issue = "35602")]

src/libcore/tests/iter.rs

+20
Original file line numberDiff line numberDiff line change
@@ -1397,6 +1397,26 @@ fn test_range_inclusive_min() {
13971397
assert_eq!(r.min(), None);
13981398
}
13991399

1400+
#[test]
1401+
fn test_range_inclusive_folds() {
1402+
assert_eq!((1..=10).sum::<i32>(), 55);
1403+
assert_eq!((1..=10).rev().sum::<i32>(), 55);
1404+
1405+
let mut it = 40..=50;
1406+
assert_eq!(it.try_fold(0, i8::checked_add), None);
1407+
assert_eq!(it, 44..=50);
1408+
assert_eq!(it.try_rfold(0, i8::checked_add), None);
1409+
assert_eq!(it, 44..=47);
1410+
1411+
let mut it = 10..=20;
1412+
assert_eq!(it.try_fold(0, |a,b| Some(a+b)), Some(165));
1413+
assert_eq!(it, 1..=0);
1414+
1415+
let mut it = 10..=20;
1416+
assert_eq!(it.try_rfold(0, |a,b| Some(a+b)), Some(165));
1417+
assert_eq!(it, 1..=0);
1418+
}
1419+
14001420
#[test]
14011421
fn test_repeat() {
14021422
let mut it = repeat(42);

0 commit comments

Comments
 (0)