Looping with Patterns in Rust: loop and while let
Master Rust pattern loops: infinite loop with break values, while let for sequence consumption, and loop labels for nested control flow.
Rust treats loops as powerful, expressive control flow constructs. Beyond standard for and while loops, Rust offers loop expressions that return values, while let for draining data with pattern matching, and labeled loops for nested jumps.
Explain Like I’m 10: The Vending Machine & Pez Dispenser#
Imagine two different snack mechanisms:
- The Vending Machine (
loopwithbreak value): You keep inserting coins in a loop until you hit the jackpot; once reached, the machine ejects your prize directly into your hands (break prize). - The Candy Dispenser (
while let Some(candy)): Every time you tilt the dispenser head back, you check if there is a candy ready. As long as candy pops out (Some(candy)), you eat it. The exact moment the dispenser is empty (None), the loop stops automatically.
graph TD
Start["Start Loop"] --> PatternMatch{"Does expression match pattern?<br/>(e.g., Some(item))"}
PatternMatch -->|Yes: Extract item| LoopBody["Execute Loop Body with item"]
LoopBody --> NextEval["Evaluate next item / state"]
NextEval --> PatternMatch
PatternMatch -->|No: None / Mismatch| Terminate["Exit Loop gracefully"]
loop as an Expression: Returning Values#
Unlike many C-style languages where loops are purely statements, Rust’s loop is an expression. You can pass a value to break to return it from the loop:
fn retry_operation() -> i32 {
let mut attempts = 0;
let result = loop {
attempts += 1;
if attempts == 3 {
// Returns 42 from the loop expression directly into 'result'
break attempts * 14;
}
};
result
}
fn main() {
let output = retry_operation();
println!("Operation result: {output}"); // 42
}rustgraph LR
LStart["Enter loop"] --> Work["Execute retry attempt"]
Work --> Check{"Success condition met?"}
Check -->|No| Work
Check -->|Yes: break value| ReturnVal["result = evaluated value"]
while let: Looping as Long as a Pattern Matches#
When popping elements from stacks, queues, channels, or iterators, traditional loops require awkward unwrapping. while let runs the loop body as long as the pattern successfully matches:
Without while let (Verbose loop + match)#
let mut stack = vec![1, 2, 3];
loop {
match stack.pop() {
Some(top) => println!("Popped: {top}"),
None => break, // Boilerplate exit
}
}rustWith while let (Clean & Idiomatic)#
let mut stack = vec![1, 2, 3];
// Loops as long as stack.pop() returns Some(top)
while let Some(top) = stack.pop() {
println!("Popped: {top}");
}rust[!TIP] Use
while letwhenever you are draining, popping, or receiving items from an iterator, channel receiver (receiver.recv()), or stack until it yieldsNoneorErr.
Loop Labels: Disambiguating Nested Loops#
When dealing with nested loops, break and continue apply to the innermost loop by default. You can specify a loop label (starting with a single quote ') to break or continue an outer loop:
fn main() {
let mut count = 0;
'counting_up: loop {
println!("count = {count}");
let mut remaining = 10;
loop {
println!("remaining = {remaining}");
if remaining == 9 {
break; // Exits the inner loop only
}
if count == 2 {
break 'counting_up; // Exits the outer loop
}
remaining -= 1;
}
count += 1;
}
println!("End count = {count}");
}rustsequenceDiagram
participant Outer as OuterLoop
participant Inner as InnerLoop
Outer->>Inner: Enter inner loop
Inner->>Inner: remaining == 9: break inner
Inner-->>Outer: Return to outer loop
Outer->>Inner: Enter inner loop with count 2
Inner->>Outer: break counting_up: Jump out of outer loop
Summary Comparison of Loop Constructs#
| Construct | Primary Use Case | Supports break value? | Pattern Matching? |
|---|---|---|---|
loop | Infinite loops, retry mechanisms, worker threads | Yes | Optional with match inside |
while condition | Standard boolean conditional looping | No | No |
while let pattern | Draining Option / Result collections | No | Yes (auto-binds) |
for item in iter | Iterating over known sequences or ranges | No | Yes (destructures items) |
Summary#
loopcan return computed values upon exit withbreak value.while letcleanly replaces verboseloop + matchboilerplate for sequence consumption.- Loop labels (
'label: loop) grant precise control over nested multi-tier loop jumps.