Control Flow

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:

  1. Place the expression you want to return immediately after the break keyword (break result;).
  2. If the loop is assigned to a variable with let, end the let statement with a semicolon ; after the closing brace } of the loop:
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)