180. Option::unzip — Split an Optional Pair Into a Pair of Options
When you have an Option<(A, B)> but the rest of your code wants (Option<A>, Option<B>), the obvious move is two map calls. Option::unzip does both halves in a single call.
The two-map dance
Say a parser returns Option<(&str, i32)> — a name and an age, or nothing. Downstream you want them as separate optional columns:
| |
That’s two passes over the same Option, two closures that throw away half their input, and an easy place to typo n for a. Option::unzip collapses it:
| |
Some((a, b)) becomes (Some(a), Some(b)). No closures, no destructuring noise.
The None case is the whole point
unzip shines on the None branch. None becomes (None, None) — both sides empty, no special-casing:
| |
Compare with the manual version: you’d still write two maps and rely on each one to short-circuit. Same behavior, more lines, more chances to drift apart when someone edits one branch and forgets the other.
Round-trip with zip
Option::zip is the inverse — it builds Option<(A, B)> from (Option<A>, Option<B>), returning Some only if both are Some:
| |
One mental model: Option<(A, B)> and (Option<A>, Option<B>) are almost the same shape, and these two methods let you flip between them without match.
When to reach for it
Any time a single optional value naturally carries two parts that the rest of the code wants to handle independently — coordinates, key/value pairs, name/age, parsed/raw. If you find yourself writing opt.map(|(x, _)| x) and opt.map(|(_, y)| y) back-to-back, that’s the signal.
Stable since Rust 1.66.