Skip to content

Peekable's .peek() does not remember seeing a None #37784

Closed
@bluss

Description

@bluss

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

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions