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.
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
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.