rustiteratorpeek

In which cases can use of peek() lead to the item being consumed?


Trying to write a parser and need to peek or consume based on what comes next in an iterator. I match on peek to make the decision and break or continue and use next right after the loop. However, peek seems to consume the items and I need some help understanding the behavior.

Also, note that in my case the iterator is passed as an impl Iterator and can not be copied - so I can not clone before peekable which seems to do the right thing.

playground link

simplified example

fn main() {
    let v = vec![0, 1, 2, 3, 4, 5];
    let mut i1 = &mut v.into_iter();
    loop {
        match i1.peekable().peek() {
            Some(p) => println!("peek {}", p),
            None => println!("peek None"),
        }

        if let Some(i) = i1.next() {
            println!("next: {}", i);
        } else {
            break;
        }
    }
}

yields

peek 0
next: 1
peek 2
next: 3
peek 4
next: 5
peek None

Solution

  • The issue is that you keep creating a new Peekable iterator inside your loop. As stated in the documentation for the peekable() function

    the underlying iterator is still advanced when peek or peek_mut are called for the first time: In order to retrieve the next element, next is called on the underlying iterator, hence any side effects (i.e. anything other than fetching the next value) of the next method will occur.

    I think you want this instead:

    fn main() {
        let v = vec![0, 1, 2, 3, 4, 5];
        let i1 = &mut v.into_iter().peekable();
        loop {
            match i1.peek() {
                Some(p) => println!("peek {}", p),
                None => println!("peek None"),
            }
    
            if let Some(i) = i1.next() {
                println!("next: {}", i);
            } else {
                break;
            }
        }
    }
    

    which correctly yields:

    peek 0
    next: 0
    peek 1
    next: 1
    peek 2
    next: 2
    peek 3
    next: 3
    peek 4
    next: 4
    peek 5
    next: 5
    peek None
    

    And the playground link.