if let, let else & while let
These constructs match one pattern when a full match is heavy. let else provides early return on mismatch.
Recipe
fn main() {
let v = Some(10);
if let Some(n) = v {
println!("{n}");
}
let Some(n) = v else {
panic!("expected Some");
};
println!("forced {n}");
}When to reach for this: Optional unwrapping, loop until None, and guard clauses with let else.
Working Example
use std::collections::VecDeque;
fn drain_even(q: &mut VecDeque<i32>) -> Vec<i32> {
let mut out = Vec::new();
while let Some(&front) = q.front() {
if front % 2 == 0 {
out.push(q.pop_front().unwrap());
} else {
break;
}
}
out
}
fn main() {
let mut q = VecDeque::from([2, 4, 5, 6]);
println!("{:?}", drain_even(&mut q));
}What this demonstrates:
while letloops while pattern matches- Combines with
breakwhen condition changes if letfor single-case branchlet elsefor required pattern (Rust 1.65+)
Deep Dive
if let
if let Err(e) = result {
eprintln!("{e}");
}let else
fn head(s: &str) -> &str {
let Some((h, _)) = s.split_once(' ') else {
return s;
};
h
}else block must diverge (return, break, panic).
while let
Ideal for consuming iterators of Option or Result until None/Err break pattern.
Gotchas
let elseelse block must diverge - Cannot fall through. Fix:return/break/panic!.- Moving in
if let- Binds move unlessref. Fix:if let Some(ref x). while letinfinite if pattern always matches - Logic bug. Fix: Ensure iterator advances or state changes.- Chaining many
if let- Hard to read. Fix: Usematchor combinator methods. if let+ else not full replacement for match - Only one positive arm ergonomic. Fix:matchfor many arms.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
match | Multiple arms | Single success path |
Option::map / and_then | Transform optional | Side effects in branches |
? on Option in fn | Propagate absence | In main without fn return |
unwrap_or / unwrap_or_else | Default value | Complex branch logic |
FAQs
`if let` vs `match`?
Use `if let` for one pattern; `match` for many.Can `let else` assign?
Yes - binds pattern variables in outer scope after success.`while let` consume iterator?
Call `next()` inside pattern: `while let Some(x) = it.next()`.Multiple patterns?
`if let (Some(a), Some(b)) = (oa, ob)` with tuple patterns.Edition 2024 changes?
Binding modes may affect move/copy in patterns - check migration notes.`else if let`?
Chain supported: `if let A(a) = x { } else if let B(b) = x { }`.Refutable let?
Only `let else` or match - plain `let` must be irrefutable.Async `while let`?
Works with async streams using `.next().await` patterns.Lint for unwrap?
clippy `manual_unwrap_or_default` etc. suggest idioms.GUI/event loops?
`while let` common for event queue processing.Related
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.