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 2260c90

Browse files
authoredAug 5, 2019
Rollup merge of rust-lang#61457 - timvermeulen:double_ended_iters, r=scottmcm
Implement DoubleEndedIterator for iter::{StepBy, Peekable, Take} Now that `DoubleEndedIterator::nth_back` has landed, `StepBy` and `Take` can have an efficient `DoubleEndedIterator` implementation. I don't know if there was any particular reason for `Peekable` not having a `DoubleEndedIterator` implementation, but it's quite trivial and I don't see any drawbacks to having it. I'm not very happy about the implementation of `Peekable::try_rfold`, but I didn't see another way to only take the value out of `self.peeked` in case `self.iter.try_rfold` didn't exit early. I only added `Peekable::rfold` (in addition to `try_rfold`) because its `Iterator` implementation has both `fold` and `try_fold` (and for similar reasons I added `Take::try_rfold` but not `Take::rfold`). Do we have any guidelines on whether we want both? If we do want both, maybe we should investigate which iterator adaptors override `try_fold` but not `fold` and add the missing implementations. At the moment I think that it's better to always have iterator adaptors implement both, because some iterators have a simpler `fold` implementation than their `try_fold` implementation. The tests that I added may not be sufficient because they're all just existing tests where `next`/`nth`/`fold`/`try_fold` are replaced by their DEI counterparts, but I do think all paths are covered. Is there anything in particular that I should probably also test?
2 parents c471519 + 56ebfb1 commit 2260c90

File tree

2 files changed

+296
-13
lines changed

2 files changed

+296
-13
lines changed
 

‎src/libcore/iter/adapters/mod.rs

+117
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,39 @@ impl<I> Iterator for StepBy<I> where I: Iterator {
485485
}
486486
}
487487

488+
impl<I> StepBy<I> where I: ExactSizeIterator {
489+
// The zero-based index starting from the end of the iterator of the
490+
// last element. Used in the `DoubleEndedIterator` implementation.
491+
fn next_back_index(&self) -> usize {
492+
let rem = self.iter.len() % (self.step + 1);
493+
if self.first_take {
494+
if rem == 0 { self.step } else { rem - 1 }
495+
} else {
496+
rem
497+
}
498+
}
499+
}
500+
501+
#[stable(feature = "double_ended_step_by_iterator", since = "1.38.0")]
502+
impl<I> DoubleEndedIterator for StepBy<I> where I: DoubleEndedIterator + ExactSizeIterator {
503+
#[inline]
504+
fn next_back(&mut self) -> Option<Self::Item> {
505+
self.iter.nth_back(self.next_back_index())
506+
}
507+
508+
#[inline]
509+
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
510+
// `self.iter.nth_back(usize::MAX)` does the right thing here when `n`
511+
// is out of bounds because the length of `self.iter` does not exceed
512+
// `usize::MAX` (because `I: ExactSizeIterator`) and `nth_back` is
513+
// zero-indexed
514+
let n = n
515+
.saturating_mul(self.step + 1)
516+
.saturating_add(self.next_back_index());
517+
self.iter.nth_back(n)
518+
}
519+
}
520+
488521
// StepBy can only make the iterator shorter, so the len will still fit.
489522
#[stable(feature = "iterator_step_by", since = "1.28.0")]
490523
impl<I> ExactSizeIterator for StepBy<I> where I: ExactSizeIterator {}
@@ -1158,6 +1191,45 @@ impl<I: Iterator> Iterator for Peekable<I> {
11581191
}
11591192
}
11601193

1194+
#[stable(feature = "double_ended_peek_iterator", since = "1.38.0")]
1195+
impl<I> DoubleEndedIterator for Peekable<I> where I: DoubleEndedIterator {
1196+
#[inline]
1197+
fn next_back(&mut self) -> Option<Self::Item> {
1198+
self.iter.next_back().or_else(|| self.peeked.take().and_then(|x| x))
1199+
}
1200+
1201+
#[inline]
1202+
fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R where
1203+
Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Ok=B>
1204+
{
1205+
match self.peeked.take() {
1206+
Some(None) => return Try::from_ok(init),
1207+
Some(Some(v)) => match self.iter.try_rfold(init, &mut f).into_result() {
1208+
Ok(acc) => f(acc, v),
1209+
Err(e) => {
1210+
self.peeked = Some(Some(v));
1211+
Try::from_error(e)
1212+
}
1213+
},
1214+
None => self.iter.try_rfold(init, f),
1215+
}
1216+
}
1217+
1218+
#[inline]
1219+
fn rfold<Acc, Fold>(self, init: Acc, mut fold: Fold) -> Acc
1220+
where Fold: FnMut(Acc, Self::Item) -> Acc,
1221+
{
1222+
match self.peeked {
1223+
Some(None) => return init,
1224+
Some(Some(v)) => {
1225+
let acc = self.iter.rfold(init, &mut fold);
1226+
fold(acc, v)
1227+
}
1228+
None => self.iter.rfold(init, fold),
1229+
}
1230+
}
1231+
}
1232+
11611233
#[stable(feature = "rust1", since = "1.0.0")]
11621234
impl<I: ExactSizeIterator> ExactSizeIterator for Peekable<I> {}
11631235

@@ -1627,6 +1699,51 @@ impl<I> Iterator for Take<I> where I: Iterator{
16271699
}
16281700
}
16291701

1702+
#[stable(feature = "double_ended_take_iterator", since = "1.38.0")]
1703+
impl<I> DoubleEndedIterator for Take<I> where I: DoubleEndedIterator + ExactSizeIterator {
1704+
#[inline]
1705+
fn next_back(&mut self) -> Option<Self::Item> {
1706+
if self.n == 0 {
1707+
None
1708+
} else {
1709+
let n = self.n;
1710+
self.n -= 1;
1711+
self.iter.nth_back(self.iter.len().saturating_sub(n))
1712+
}
1713+
}
1714+
1715+
#[inline]
1716+
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
1717+
let len = self.iter.len();
1718+
if self.n > n {
1719+
let m = len.saturating_sub(self.n) + n;
1720+
self.n -= n + 1;
1721+
self.iter.nth_back(m)
1722+
} else {
1723+
if len > 0 {
1724+
self.iter.nth_back(len - 1);
1725+
}
1726+
None
1727+
}
1728+
}
1729+
1730+
#[inline]
1731+
fn try_rfold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R where
1732+
Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try<Ok = Acc>
1733+
{
1734+
if self.n == 0 {
1735+
Try::from_ok(init)
1736+
} else {
1737+
let len = self.iter.len();
1738+
if len > self.n && self.iter.nth_back(len - self.n - 1).is_none() {
1739+
Try::from_ok(init)
1740+
} else {
1741+
self.iter.try_rfold(init, fold)
1742+
}
1743+
}
1744+
}
1745+
}
1746+
16301747
#[stable(feature = "rust1", since = "1.0.0")]
16311748
impl<I> ExactSizeIterator for Take<I> where I: ExactSizeIterator {}
16321749

0 commit comments

Comments
 (0)
Please sign in to comment.