`while` and `loop`

Essential
Last updated: Tags: Rust

Prerequisites

When you need a loop whose iteration count is not known in advance — or potentially infinite — Rust gives you three constructs: while, loop, and while let.

while

while evaluates a condition before each iteration and exits as soon as it is false:

let mut n = 1;
while n < 100 {
    n *= 2;
}
println!("{n}"); // 128

Like if, the condition must be a bool. Rust does not coerce integers or pointers to boolean.

loop

loop runs forever until you break out of it:

loop {
    let line = read_line();
    if line == "quit" {
        break;
    }
    process(line);
}

Because the compiler knows loop always executes at least once, it is the only loop construct that can produce a value. Pass the value to break:

let result = loop {
    let candidate = next_candidate();
    if is_valid(candidate) {
        break candidate; // becomes the value of the whole `loop` expression
    }
};

while and while let cannot do this — their condition might be false from the start, so there is no guaranteed value to produce.

Retry and event loops

loop + break value is the idiomatic shape for retry patterns:

let connection = loop {
    match try_connect() {
        Ok(conn) => break conn,
        Err(e) => {
            eprintln!("retrying after error: {e}");
            std::thread::sleep(std::time::Duration::from_secs(1));
        }
    }
};

For event loops that should run until a termination signal, loop with a break condition reads more clearly than a while built around a mutable flag.

while let

while let loops as long as a pattern continues to match:

let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
    println!("{top}");
}

stack.pop() returns Option<i32>: Some(v) while elements remain, then None. When pop() returns None, the pattern stops matching and the loop ends.

This is equivalent to a loop containing match + break, but the intent is clearer at a glance. It is the natural companion to if let — same desugaring, repeated instead of conditional.

continue and break with labels

Nested loops can be hard to break out of. Labels target a specific loop:

'outer: for row in &grid {
    for &cell in row {
        if cell == target {
            break 'outer; // exits both loops
        }
    }
}

Labels start with a single quote and precede the loop keyword. break 'label value combines a label with a value for labeled loop expressions.