C.W.K.
Stream
Lesson 04 of 06 · published

if let, let else, while let

~10 min · enums, if-let, let-else, while-let

Level 0Rust Curious
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

match is exhaustive, which is powerful but verbose when you care about exactly one pattern. The let-family of constructs are the ergonomic shortcuts for that common case.

if let — one pattern, ignore the rest

if let Some(x) = opt { ... } runs the block only when the pattern matches, binding x for use inside. Everything else is silently skipped. You can add an else for the non-matching case. It's exactly a match with one real arm and a _ => {}, but without the ceremony.

let else — match or bail out

let Some(x) = opt else { return; }; binds x if the pattern matches, and otherwise runs the else block, which must diverge (return, break, panic). After this line, x is available in the rest of the function — no nesting. It's the clean way to handle 'if this isn't present, stop here' at the top of a function.

let else flattens the happy path. Instead of wrapping your whole function body in if let { ... }, use let ... else { return } to handle the bad case early and keep the main logic un-indented. It's the Rust answer to the early-return guard clause.

while let — loop until the pattern fails

while let Some(x) = iter.next() { ... } keeps looping as long as the pattern matches, stopping the moment it gets None. It's the natural way to drain a stack, consume an iterator manually, or process a queue until empty.

Code

if let, let else, and while let·rust
fn main() {
    let config: Option<i32> = Some(42);

    // if let: act only on Some, ignore None
    if let Some(v) = config {
        println!("configured: {v}");
    }

    // let else: bind or bail (the else MUST diverge)
    let Some(v) = config else {
        println!("no config");
        return;
    };
    println!("using {v}"); // v is in scope, no nesting

    // while let: loop until the pattern stops matching
    let mut stack = vec![1, 2, 3];
    while let Some(top) = stack.pop() {
        println!("popped {top}");
    }
}

External links

Exercise

Take a function that receives an Option<String> config. Write it once with a nested if let, then rewrite it with let ... else { return } so the happy path is un-indented. Which version is easier to read when the function grows to 30 lines?
Hint
let else handles the absent case at the top and lets the rest of the function run flat. Nested if let pushes the main logic one indent deeper for every optional value — it stacks up fast.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue
💛 by Ttoriwarm

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.