Enums and Pattern Matching

while let

A while let loop repeatedly evaluates a scrutinee expression, matches it against a refutable pattern, and executes the loop body for as long as the pattern continues to match—exiting the loop as soon as the pattern fails to match.

Equivalence to a loop with match

// Manual `loop` + `match`:                  // Idiomatic `while let`:
loop {                                       while let Some(item) = stack.pop() {
    match stack.pop() {                          process(item);
        Some(item) => process(item),         }
        None => break,
    }
}

Draining a Stack or Queue with while let

Unlike a for x in collection loop (which creates an iterator once at the start of the loop), while let Some(x) = stack.pop() re-evaluates stack.pop() on every iteration, allowing the loop body to push new work items onto stack dynamically (such as during a depth-first graph or tree traversal):

fn main() {
    let mut work_stack = vec![1, 2, 3];

    while let Some(task) = work_stack.pop() {
        println!("Processing task #{task}");
        // Dynamically enqueue a follow-up subtask while iterating:
        if task == 2 {
            work_stack.push(20);
        }
    }
}
Processing task #3
Processing task #2
Processing task #20
Processing task #1

Inspecting an Iterator Manually Inside a while let Loop

When you need to advance an iterator conditionally inside the loop body (for example, consuming a lookahead token in a lexer or command-line argument parser), while let Some(token) = iter.next() lets you call iter.next() again inside the body without borrow conflicts:

fn parse_flags(args: &[&str]) {
    let mut iter = args.iter().copied();

    while let Some(arg) = iter.next() {
        match arg {
            "-o" | "--output" => {
                // Consume the next item from the same iterator as the flag's value:
                if let Some(filename) = iter.next() {
                    println!("Output file set to: {filename}");
                } else {
                    println!("Error: {arg} requires a filename");
                }
            }
            "--verbose" => println!("Verbose mode enabled"),
            positional => println!("Positional argument: {positional}"),
        }
    }
}

fn main() {
    parse_flags(&["--verbose", "-o", "app.bin", "src/main.rs"]);
}
Verbose mode enabled
Output file set to: app.bin
Positional argument: src/main.rs

Walking Linked Structures via Nested References

while let is also the idiomatic way to walk an Option<Box<Node>> chain or peel nested wrappers until None is reached:

struct Node {
    value: i32,
    next: Option<Box<Node>>,
}

fn main() {
    let list = Some(Box::new(Node {
        value: 10,
        next: Some(Box::new(Node {
            value: 20,
            next: Some(Box::new(Node {
                value: 30,
                next: None,
            })),
        })),
    }));

    let mut current = list.as_deref();
    while let Some(node) = current {
        print!("{} -> ", node.value);
        current = node.next.as_deref();
    }
    println!("None");
}
10 -> 20 -> 30 -> None