diff --git a/src/adapters/enumerate.rs b/src/adapters/enumerate.rs new file mode 100644 index 0000000..1addcec --- /dev/null +++ b/src/adapters/enumerate.rs @@ -0,0 +1,82 @@ +use crate::LendingIterator; + +/// A lending iterator that yields the current count and the element during iteration. +pub struct Enumerate { + count: usize, + iter: I, +} + +impl Enumerate { + pub(crate) fn new(iter: I) -> Self { + Enumerate { iter, count: 0 } + } +} + +impl LendingIterator for Enumerate { + type Item<'a> = (usize, I::Item<'a>) where Self: 'a; + fn next(&mut self) -> Option> { + let item = self.iter.next()?; + let count = self.count; + self.count += 1; + Some((count, item)) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::ToLendingIterator; + + // A non-fused iterator for testing that we match std's behavior + struct Delay { + countdown: usize, + iter: I, + } + + impl Delay { + fn new(countdown: usize, iter: I) -> Self { + Delay { countdown, iter } + } + } + + // Generally we avoid implementing both Iterator and LendingIterator + // for the same type. Here in testing the bounds of the arguments + // are known not to collide. + impl LendingIterator for Delay { + type Item<'a> = I::Item<'a> where Self: 'a; + fn next(&mut self) -> Option> { + if self.countdown == 0 { + self.iter.next() + } else { + self.countdown -= 1; + None + } + } + } + + impl Iterator for Delay { + type Item = I::Item; + fn next(&mut self) -> Option { + if self.countdown == 0 { + self.iter.next() + } else { + self.countdown -= 1; + None + } + } + } + + #[test] + fn test() { + let first = Some((0, ())); + let second = Some((1, ())); + let mut delay_iter = Delay::new(1, std::iter::repeat(()).take(2)).enumerate(); + let mut delay_lending = + Delay::new(1, std::iter::repeat(()).into_lending().take(2)).enumerate(); + + assert_eq!((None, None), (delay_iter.next(), delay_lending.next())); + assert_eq!((first, first), (delay_iter.next(), delay_lending.next())); + assert_eq!((second, second), (delay_iter.next(), delay_lending.next())); + assert_eq!((None, None), (delay_iter.next(), delay_lending.next())); + } +} diff --git a/src/adapters/mod.rs b/src/adapters/mod.rs index df9054d..f1f965a 100644 --- a/src/adapters/mod.rs +++ b/src/adapters/mod.rs @@ -1,5 +1,6 @@ mod chain; mod cloned; +mod enumerate; mod filter; mod filter_map; mod map; @@ -8,6 +9,7 @@ mod take; mod zip; pub use self::chain::Chain; pub use self::cloned::Cloned; +pub use self::enumerate::Enumerate; pub use self::filter::Filter; pub use self::filter_map::FilterMap; pub use self::map::{IntoIter, Map}; diff --git a/src/traits/lending_iterator.rs b/src/traits/lending_iterator.rs index b2c58ba..1b5ad88 100644 --- a/src/traits/lending_iterator.rs +++ b/src/traits/lending_iterator.rs @@ -1,8 +1,8 @@ use std::{num::NonZeroUsize, ops::Deref}; use crate::{ - Chain, Cloned, Filter, FilterMap, Map, OptionTrait, SingleArgFnMut, SingleArgFnOnce, StepBy, - Take, Zip, + Chain, Cloned, Enumerate, Filter, FilterMap, Map, OptionTrait, SingleArgFnMut, SingleArgFnOnce, + StepBy, Take, Zip, }; /// Like [`Iterator`], but items may borrow from `&mut self`. @@ -191,4 +191,12 @@ pub trait LendingIterator { { Cloned::new(self) } + + /// Creates a lending iterator which gives the current iteration count as well as the next value. + fn enumerate(self) -> Enumerate + where + Self: Sized, + { + Enumerate::new(self) + } }