Returning Values from Loops
In Rust, loop is an expression, not merely a control-flow statement. When you terminate a loop using break <expression>;, the loop evaluates to that expression's value, which can be assigned to a let binding, passed to a function, or returned directly as a function's tail expression.
Basic break value Syntax in loop
To return a value from a loop:
- Place the expression you want to return immediately after the
breakkeyword (break result;). - If the
loopis assigned to a variable withlet, end theletstatement with a semicolon;after the closing brace}of theloop:
let output = loop {
...
if done {
break computed_value; ---+
} |
}; <-----------------------------+ (`output` receives `computed_value`)
fn main() {
let mut counter = 0;
let result: i32 = loop {
counter += 1;
if counter == 10 {
break counter * 2; // Exits the loop and produces `20`
}
};
println!("Loop evaluated to: {result}");
}
Loop evaluated to: 20
Using let result = loop { ... break value; }; avoids declaring an uninitialized or mutable let mut result = ...; variable outside the loop just to smuggle a result out of the loop body.
Multiple break Branches with Consistent Types
A loop can contain multiple break statements across different branches (for example, returning Some(item) when a search succeeds and None when the search space is exhausted). Every break targeting that loop must return the exact same type:
fn find_square_root_exact(target: u64) -> Option<u64> {
let mut candidate = 0;
// The `loop` expression is the tail expression of the function!
loop {
let square = candidate * candidate;
if square == target {
break Some(candidate); // Branch 1: returns `Option<u64>`
}
if square > target {
break None; // Branch 2: also returns `Option<u64>`
}
candidate += 1;
}
}
fn main() {
println!("sqrt(49) = {:?}", find_square_root_exact(49));
println!("sqrt(50) = {:?}", find_square_root_exact(50));
}
sqrt(49) = Some(7)
sqrt(50) = None
Returning a Value from an Outer Labeled Loop (break 'label value)
When searching a 2D grid or nested structure using nested loop blocks, you can return a value directly from the outer loop by placing both the loop label and the value after break:
break 'outer_label value_expression;
──────────── ────────────────
│ └── Value produced by the `'outer_label` loop
└───────────────── Target loop to terminate
fn main() {
let grid = [
[10, 20, 30],
[40, -5, 60],
[70, 80, 90],
];
let mut r = 0;
let first_negative: Option<(usize, usize, i32)> = 'row_search: loop {
if r >= grid.len() {
break 'row_search None;
}
let mut c = 0;
loop {
if c >= grid[r].len() {
break; // Breaks ONLY the inner column loop (returns `()`)
}
if grid[r][c] < 0 {
// Breaks the OUTER `'row_search` loop with `Some(...)`!
break 'row_search Some((r, c, grid[r][c]));
}
c += 1;
}
r += 1;
};
println!("First negative entry: {first_negative:?}");
}
First negative entry: Some((1, 1, -5))
Notice that the inner break; (with no label and no value) exits only the inner loop (which evaluates to ()), whereas break 'row_search Some(...) exits the outer loop and supplies its Option<(usize, usize, i32)> value.
Why while and for Loops Cannot Return Values (E0571)
Only loop (and labeled blocks 'b: { ... }) can evaluate to a non-() value via break value;. Both while and for loops always evaluate to ():
| Loop Construct | Can Condition/Iterator Finish Without Hitting break? |
Can Use break value;? |
Expression Type |
|---|---|---|---|
loop { ... } |
No (can only exit via break or return) |
Yes | Type of value (or ! if no break) |
while cond { ... } |
Yes (when cond evaluates to false) |
No (E0571) |
Always () |
for x in iter { ... } |
Yes (when iter.next() returns None) |
No (E0571) |
Always () |
If while or for allowed break 42;, there would be no value to assign when the loop condition initially evaluated to false or the iterator was empty:
// ❌ does not compile
fn main() {
let found = for x in [1, 2, 3] {
if x == 2 {
break x;
}
};
}
error[E0571]: `break` with value from a `for` loop
--> src/main.rs:4:13
|
2 | let found = for x in [1, 2, 3] {
| ------------------ you can't `break` with a value in a `for` loop
3 | if x == 2 {
4 | break x;
| ^^^^^^^ can only break with a value inside `loop` or breakable block
|
help: use `break` on its own without a value inside this `for` loop
|
4 - break x;
4 + break;
error: aborting due to 1 previous error
Workaround: Wrapping a for or while Loop Inside a Labeled Block
If you want to search a for loop and evaluate to a default value if the loop finishes normally without breaking, wrap the for loop inside a labeled block 'search: { ... } and use break 'search value;:
fn main() {
let numbers = [3, 7, 12, 19];
let first_even: Option<i32> = 'search: {
for n in numbers {
if n % 2 == 0 {
break 'search Some(n); // Exits the `'search` block with `Some(12)`
}
}
None // Tail expression of `'search` block if the `for` loop finishes normally!
};
println!("First even number: {first_even:?}");
}
First even number: Some(12)