Need to combine two Option values into a pair? Option::zip merges them into a single Option<(A, B)> — if either is None, you get None back.
The problem
You have two optional values and need both to proceed. The classic approach uses nested matching:
1
2
3
4
5
6
7
8
9
10
11
12
13
| let name: Option<&str> = Some("Alice");
let age: Option<u32> = Some(30);
// Nested match — gets unwieldy fast
let greeting = match name {
Some(n) => match age {
Some(a) => Some(format!("{n} is {a} years old")),
None => None,
},
None => None,
};
assert_eq!(greeting, Some("Alice is 30 years old".to_string()));
|
The fix: Option::zip
Zip collapses two Options into one tuple:
1
2
3
4
5
6
| let name: Option<&str> = Some("Alice");
let age: Option<u32> = Some(30);
let greeting = name.zip(age).map(|(n, a)| format!("{n} is {a} years old"));
assert_eq!(greeting, Some("Alice is 30 years old".to_string()));
|
One line instead of six. If either value is None, zip short-circuits to None:
1
2
3
4
| let name: Option<&str> = Some("Alice");
let age: Option<u32> = None;
assert_eq!(name.zip(age), None);
|
Bonus: zip with and_then
You can chain zip into more complex pipelines:
1
2
3
4
5
6
7
8
9
10
11
12
13
| fn lookup_user(id: u32) -> Option<String> {
if id == 1 { Some("Alice".to_string()) } else { None }
}
fn lookup_role(id: u32) -> Option<String> {
if id == 1 { Some("Admin".to_string()) } else { None }
}
let result = lookup_user(1)
.zip(lookup_role(1))
.map(|(user, role)| format!("{user} ({role})"));
assert_eq!(result, Some("Alice (Admin)".to_string()));
|
Option::zip is stable since Rust 1.46 and works anywhere you need both-or-nothing semantics without the nesting.