Control Flow

break

The break keyword immediately terminates the execution of the innermost enclosing loop, while, or for loop (or a labeled block/loop specified by a 'label) and transfers control to the first statement following the terminated block.

Forms of break

Rust supports four syntactic forms of break:

Form Valid Inside Effect
break; loop, while, for Exits the innermost enclosing loop immediately
break value; loop only Exits the innermost loop and makes that loop expression evaluate to value
break 'label; Labeled loop, while, for, or 'label: { ... } block Exits the specific enclosing loop or block named 'label
break 'label value; Labeled loop or labeled 'label: { ... } block Exits the target loop/block named 'label and makes it evaluate to value

Basic break in loop, while, and for

When written without a label or value, break; exits whichever loop most closely encloses it:

fn main() {
    let readings = [12, 18, 25, 999, 14, 19];
    let mut valid_sum = 0;

    for reading in readings {
        if reading == 999 {
            println!("Encountered sentinel 999; stopping loop.");
            break;
        }
        valid_sum += reading;
    }

    println!("Sum of readings before sentinel: {valid_sum}");
}
Encountered sentinel 999; stopping loop.
Sum of readings before sentinel: 55
for reading in [12, 18, 25, 999, 14, 19] {
    reading = 12  --> valid_sum = 12
    reading = 18  --> valid_sum = 30
    reading = 25  --> valid_sum = 55
    reading = 999 --> `break;` executed! ---+
}                                           |
println!(...);  <---------------------------+ (Control resumes here)

Innermost Loop Rule in Nested Loops

In nested loops, an unlabeled break; exits only the innermost loop in which it appears; the outer loop continues executing its remaining iterations:

fn main() {
    for row in 1..=3 {
        print!("Row {row}: ");
        for col in 1..=5 {
            if col == 3 {
                break; // Exits only the inner `col` loop!
            }
            print!("col={col} ");
        }
        println!("(row finished)");
    }
}
Row 1: col=1 col=2 (row finished)
Row 2: col=1 col=2 (row finished)
Row 3: col=1 col=2 (row finished)

To break out of an outer loop from inside an inner loop, attach a loop label ('outer: for ...) and write break 'outer;.

Breaking with a Value (break expr;)

Inside a loop expression, break can pass back a value that becomes the result of the entire loop:

fn main() {
    let mut candidate = 100;

    let first_divisible_by_17 = loop {
        if candidate % 17 == 0 {
            break candidate; // Terminates `loop` and evaluates it to `102`
        }
        candidate += 1;
    };

    println!("First number >= 100 divisible by 17: {first_divisible_by_17}");
}
First number >= 100 divisible by 17: 102

All break statements that exit the same loop must supply values of the same type (because a break; with no value implicitly supplies ()).

Breaking from a Labeled Block ('block: { ... })

In addition to loops, Rust allows attaching a label 'label: to an ordinary lexical block 'label: { ... } and using break 'label (with or without a value) to exit that block early without exiting the entire enclosing function:

fn process_record(raw: &str) {
    let parsed_id: u32 = 'validation: {
        let trimmed = raw.trim();
        if trimmed.is_empty() {
            println!("Empty input; using default ID 0");
            break 'validation 0; // Exits the `'validation` block early with `0`
        }

        match trimmed.parse::<u32>() {
            Ok(id) => id,
            Err(_) => {
                println!("Invalid number '{trimmed}'; using default ID 0");
                break 'validation 0;
            }
        }
    };

    println!("Continuing function with parsed_id = {parsed_id}");
}

fn main() {
    process_record("  42 ");
    process_record("   ");
}
Continuing function with parsed_id = 42
Empty input; using default ID 0
Continuing function with parsed_id = 0

Resource Cleanup (Drop) When break Executes

When break jumps out of a loop or labeled block, Rust automatically drops all local variables currently alive inside the exited block(s) in reverse declaration order (LIFO) before control reaches the statement after the loop.

Common Errors

Using Unlabeled break Outside of a Loop (E0268)

An unlabeled break cannot be used inside a plain if or match block unless that if or match is inside a loop (unlike C and JavaScript, match arms do not fall through and never use break to end a match arm):

// ❌ does not compile
fn main() {
    let x = 2;
    match x {
        1 => println!("one"),
        2 => {
            println!("two");
            break;
        }
        _ => println!("other"),
    }
}
error[E0268]: `break` outside of a loop or labeled block
 --> src/main.rs:8:13
  |
8 |             break;
  |             ^^^^^ cannot `break` outside of a loop or labeled block

error: aborting due to 1 previous error

Inconsistent Types Across break Statements in a loop (E0308)

If one break inside a loop returns a value (break 42;) while another break omits a value (break;, which returns ()), the compiler reports a type mismatch:

// ❌ does not compile
fn main() {
    let mut n = 0;
    let value: i32 = loop {
        n += 1;
        if n == 3 {
            break 30;
        }
        if n > 5 {
            break;
        }
    };
}
error[E0308]: `break` with value from a `loop` must have the same type as other `break`s
  --> src/main.rs:10:13
   |
 4 |     let value: i32 = loop {
   |                --- expected `i32` because of this
...
 7 |             break 30;
   |                   -- expected because of this
...
10 |             break;
   |             ^^^^^ implicitly returns `()` as its body has no tail or `return` expression, expected `i32`
   |
help: give it a value of the expected type
   |
10 |             break /* value */;
   |                  ++++++++++++

error: aborting due to 1 previous error