TL;DR:
if let Some(x) = optional { use(x) }is shorthand for amatchwith one useful arm and a fallback. It extracts values fromOption,Result, or any enum variant without the noise of a fullmatch. Useif letwhen you only care about one pattern. Usematchwhen you need to handle all variants. The relatedwhile letloops until a pattern stops matching.let-else(Rust 1.65+) is the inverse: unwrap or return early.
What Is if let?
if let matches a single pattern and executes the body if it matches; ignoring all other variants.
// Without if let; verbose match
let config = get_config();
match config {
Some(cfg) => println!("host: {}", cfg.host),
None => {} // nothing to do for None
}
// With if let; cleaner
if let Some(cfg) = get_config() {
println!("host: {}", cfg.host);
}It also works with Result:
if let Ok(data) = std::fs::read_to_string("config.toml") {
println!("loaded {} bytes", data.len());
}
if let Err(e) = validate_input(&input) {
eprintln!("validation failed: {e}");
return;
}How Do You Add an else Branch?
if let supports an else clause for the non-matching case.
if let Some(user) = find_user(id) {
println!("found: {}", user.name);
} else {
println!("user not found");
}
// Equivalent match:
match find_user(id) {
Some(user) => println!("found: {}", user.name),
None => println!("user not found"),
}How Do You Match Nested or Complex Patterns?
if let supports the same patterns as match; destructuring, guards, and nested enums.
#[derive(Debug)]
enum Response {
Success { code: u16, body: String },
Error { code: u16, message: String },
Timeout,
}
let response = get_response();
// Destructure a struct variant
if let Response::Success { code, body } if code == 200 = response {
println!("OK: {body}");
}
// Nested Option
if let Some(Some(inner)) = nested_option {
println!("deep value: {inner}");
}
// Tuple
if let (Some(x), Some(y)) = (opt_x, opt_y) {
println!("both present: {x}, {y}");
}What Is while let?
while let loops as long as a pattern keeps matching; commonly used to drain iterators, channels, or stacks.
use tokio::sync::mpsc;
let (tx, mut rx) = mpsc::channel::<String>(32);
// Process messages until channel is closed
while let Some(message) = rx.recv().await {
println!("received: {message}");
}
// Pop from a stack until empty
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("popped: {top}");
}What Is let-else (the Inverse)?
let-else (Rust 1.65+) unwraps a pattern or runs a diverging block; the idiomatic way to return/break early on mismatch.
// if let; happy path inside the block
if let Some(user) = find_user(id) {
// ... 5 levels of indentation later
}
// let-else; happy path continues at same level
let Some(user) = find_user(id) else {
return Err("user not found");
};
// user is available here, unindented
println!("user: {}", user.name);let-else requires the else block to diverge (return, break, continue, panic!): it can't fall through.
When to Use if let vs match?
| Situation | Use |
|---|---|
| Only one pattern matters | if let |
| Need to handle all variants | match |
| Want an else branch too | if let ... else or match |
| Unwrap or return early | let-else |
| Loop until pattern fails | while let |
Multiple patterns with | | match |
Frequently Asked Questions
Yes, with | in the pattern:
if let Status::Active | Status::Pending = user.status {
process(&user);
}Yes; if let is an expression and works anywhere an expression is valid, including closures and iterator adapters.
.map() transforms a value inside Option or Result without leaving the monad. if let extracts the value for use in imperative code. Use .map() for functional chains; use if let when you need side effects or early returns.
No. if let is a language construct, not a method. There's no .if_let() method. Some codebases use .map(|v| { ... }) or .is_some_and(|v| ...) for similar patterns.
Sources
Related Glossary Terms
- Pattern Matching:
if letis a shorthand pattern match - Option: The most common target for
if let - Result:
if let Ok(v)/if let Err(e)patterns - Enum:
if letworks on any enum variant
Keep Reading
- Rust Error Handling: Result and Option: if let is the ergonomic way to handle Option and Result
- Rust Ownership and Borrowing Explained: pattern matching and ownership
- Rust for Python Developers: Python's match statement vs Rust's if let
