Iterators

peekable()

The .peekable() adapter wraps an iterator in a std::iter::Peekable<Self> struct that adds one-element lookahead via .peek(), allowing you to inspect the next element by reference (Option<&Self::Item>) without consuming it.

Key Methods on Peekable<I>

Method Signature Behavior
.peek() fn peek(&mut self) -> Option<&I::Item> Pulls and caches the next item if not already buffered, returning a shared reference to it
.peek_mut() fn peek_mut(&mut self) -> Option<&mut I::Item> Returns a mutable reference to the peeked item, allowing in-place modification before .next()
.next_if(pred) fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option<I::Item> Consumes and returns the next item only if func(&item) returns true
.next_if_eq(expected) fn next_if_eq<T>(&mut self, expected: &T) -> Option<I::Item> Consumes and returns the next item only if it equals expected
Underlying Iterator: [ 'b', 'c' ]
Peekable Buffer:     Some('a')   <-- .peek() returns Some(&'a') without advancing
                                 <-- .next() takes Some('a') from buffer

Notice that .peek() takes &mut self even though it returns an immutable reference Option<&I::Item>, because the first call to .peek() must call .next() on the underlying iterator and store the item inside Peekable's internal Option<Option<I::Item>> cache.

Lexing or Grouping Consecutive Runs with .peek() and .next_if_eq()

Peekable is essential in tokenizers, parsers, and run-length encoders where deciding whether to consume the next item depends on what it is:

fn run_length_encode(input: &str) -> Vec<(char, usize)> {
    let mut chars = input.chars().peekable();
    let mut runs = Vec::new();

    while let Some(ch) = chars.next() {
        let mut count = 1;
        // Consume subsequent identical characters using `next_if_eq`:
        while chars.next_if_eq(&ch).is_some() {
            count += 1;
        }
        runs.push((ch, count));
    }

    runs
}

fn main() {
    let encoded = run_length_encode("aaabbcdddd");
    println!("{encoded:?}");
}
[('a', 3), ('b', 2), ('c', 1), ('d', 4)]

Inspecting Without Consuming via .peek()

fn main() {
    let mut nums = [10, 20, 30].into_iter().peekable();

    println!("Peek 1: {:?}", nums.peek());
    println!("Peek 2: {:?}", nums.peek()); // Still 10!
    println!("Next 1: {:?}", nums.next()); // Consumes 10
    println!("Peek 3: {:?}", nums.peek()); // Now 20
}
Peek 1: Some(&10)
Peek 2: Some(&10)
Next 1: Some(10)
Peek 3: Some(&20)