Closed
Description
If you use iterator.peekable()
, call .peek()
and see None
, peekable does not remember that.
This means that the next call to either .peek()
or .next()
will query the underlying iterator again, making it easy to create fusing bugs. (A well behaved iterator user should not call .next()
on a generic iterator that has already returned None
once.)
Example program which ends up being an infinite loop (the while let loop). (playground link)
/// This is an iterator that is in line with the contract
/// of the Iterator trait, but it is not fused.
/// After having returned None once, it will start producing elements
/// if .next() is called again.
pub struct CycleIter<'a, T: 'a> {
index: usize,
data: &'a [T],
}
pub fn cycle<T>(data: &[T]) -> CycleIter<T> {
CycleIter {
index: 0,
data: data,
}
}
impl<'a, T> Iterator for CycleIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
let elt = self.data.get(self.index);
self.index += 1;
self.index %= 1 + self.data.len();
elt
}
}
fn main() {
let data = [1, 2, 3];
// show that it works like a regular iterator
for elt in cycle(&data) {
print!("{}, ", elt);
}
println!("");
// Demonstrate that it's easy to create bugs with peekable
let mut iter = cycle(&data).peekable();
while let Some(elt) = iter.next() {
let is_the_last = iter.peek().is_none();
println!("Saw element={}, is the last={:?}", elt, is_the_last);
}
}
Metadata
Metadata
Assignees
Labels
No labels