Rust Let Else
let ... else unwraps a pattern and keeps the bound names for the rest of the function. The else block runs when the pattern fails.
The nested way: if let wraps the work
With if let, the names live only inside the block, so the real work nests inside it:
let input: Option<&str> = Some("tea");
if let Some(drink) = input {
println!("drink = {drink}");
} else {
println!("no drink chosen");
}Bind the names, then just carry on
The names bound by let ... else live in the code after the line — no extra block, no nesting. The else side runs only when the pattern fails:
let input: Option<&str> = Some("tea");
let Some(drink) = input else {
println!("no drink chosen");
return;
};
println!("drink = {drink}");The else block must leave
The else block must diverge — leave by return, break, continue or panic!, and never fall through to the next line. One that just prints and ends is refused:
let Some(x) = input else {
println!("nothing here");
};
// error E0308: `else` clause of `let...else` does not divergeA whole tuple pattern in one line
Two Options can be tested together in a single line. Both names bind, or the else block leaves:
let pair = (Some(1), Some(2));
let (Some(a), Some(b)) = pair else {
println!("both are needed");
return;
};
println!("{} + {} = {}", a, b, a + b);It works for Result too
An Ok pattern keeps the good value on the main path, and the bad case leaves early:
let text = "42";
let Ok(count) = text.parse::<u32>() else {
println!("not a number");
return;
};
println!("count = {count}");Example
fn split_count(text: &str) -> (u64, &str) {
let mut parts = text.splitn(2, ' ');
let (Some(count_text), Some(item)) = (parts.next(), parts.next()) else {
return (0, "");
};
let Ok(count) = count_text.parse::<u64>() else {
return (0, "");
};
(count, item)
}
fn main() {
let (n, item) = split_count("3 chairs");
println!("{} x {}", n, item);
}Key points
- let ... else has been stable since Rust 1.65, so it works in any current project
In this example, split_count reads text like "3 chairs". The first let ... else tests two Options at once: (Some(count_text), Some(item)). Both are present. count_text is "3" and item is "chairs". Both names stay alive for the rest of the function. The second let ... else parses "3" as a number with an Ok pattern. It succeeds, so count is 3. The function returns (3, "chairs"), and main prints 3 x chairs. Had either step failed, its else block would have left the function at once, handing back the empty pair.
Note
Reach for let ... else at the top of a function, as a gate.
Reach for if let when both sides have real work to do, not just an early exit.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
What must the else block of a let ... else always do?