203. Peekable::next_if_map — Consume a Token Only If It Parses, Transform in One Step
next_if only answers yes/no, so when you also need the converted value you end up peeking, computing, and calling next() by hand. Rust 1.94 stabilized Peekable::next_if_map — conditionally consume the next item and transform it in a single call, putting the item back if it doesn’t match.
The trap: the peek / compute / advance dance
Hand-rolled lexers are full of this pattern — look at the next item, decide whether it’s the kind you want, and only then consume it. With next_if you can express the decide part, but next_if hands you back the original item, so you have to redo the conversion afterward. Most people skip it and drop down to a manual peek() + next():
| |
The conversion (to_digit) and the consumption (next) are split across separate lines, and the iterator only advances as a side effect. Forget the it.next() and you’ve written an infinite loop.
The fix: decide and transform in one call
next_if_map takes the next item by value and hands it to a closure returning Result<R, Item>. Return Ok(value) and the item is consumed, giving you Some(value); return Err(item) and the item is pushed back, giving you None. The classic conversion-or-give-back is just .ok_or(c):
| |
One line, no manual next(), and the “advance only on success” rule is enforced by the method instead of by you remembering to call it.
It really does put the item back
When the closure returns Err, the iterator is left exactly where it was — the next read still sees that item:
| |
That give-it-back guarantee is what makes it safe to chain in a loop: each call either makes progress or leaves the stream untouched for the next rule to try.
Where it shines: tokenizing
A digit-run parser becomes a tight while let that stops cleanly at the first non-digit, leaving the rest of the input for whatever comes next:
| |
There’s also next_if_map_mut, which passes &mut Item and takes a closure returning Option<R> — handy when the item is expensive to move or you want to mutate it in place rather than hand it back.
The bottom line
When you only want the next item if it converts to something useful, reach for next_if_map instead of the peek / compute / next shuffle. It folds the test and the transform into one call and guarantees the iterator only advances when the conversion succeeds — exactly the invariant hand-written lexers keep getting wrong.