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