# 7.6 break, continue, and loop labels
> Ending loops early, skipping iterations, break-with-value from loop, and labeled breaks for nested loops.
Source: https://learnrust.net/chapter-7/break-continue-and-loop-labels/
Lesson [7.4](@/chapter-7/loop-and-while.md) used `break` on a promise that the full story would come later. It's later. This lesson covers the two jump statements that steer loops from the inside, plus the trick that's exclusive to `loop` and the tool that retired goto.
## break
A **break statement** immediately ends the loop it appears in; execution continues at the first statement after the loop's block. It works in all three loops. The classic use is a sentinel, a special value that means "stop":
```rust
use std::io;
fn main() {
let mut sum = 0;
loop {
println!("Enter a number to add, or 0 to finish:");
let mut input = String::new();
io::stdin().read_line(&mut input).expect("failed to read line");
let n: i32 = input.trim().parse().expect("please type a number");
if n == 0 {
break;
}
sum += n;
}
println!("The sum is: {sum}");
}
```
```
Enter a number to add, or 0 to finish:
5
Enter a number to add, or 0 to finish:
12
Enter a number to add, or 0 to finish:
0
The sum is: 17
```
Don't confuse `break` with `return`. A `break` ends the *loop*; a `return` ends the whole *function*, loop and all (lesson [2.2](@/chapter-2/return-values.md)). Inside `main`'s loop the difference is invisible (there's nothing after the loop but the end of the program), so here it is somewhere it matters:
```rust
fn first_square_over(limit: i32) -> i32 {
let mut n = 1;
loop {
if n * n > limit {
return n; // exits first_square_over entirely
}
n += 1;
}
}
fn main() {
println!("{}", first_square_over(300));
}
```
```
18
```
The `return` doesn't just stop the loop; it delivers the function's answer from the middle of it. (17² is 289, 18² is 324, so 18 it is.)
## continue
A **continue statement** ends the current *iteration*, not the loop: execution jumps straight to the loop's next go-around. Use it to skip values that don't interest you:
```rust
fn main() {
for i in 1..=10 {
if i % 4 == 0 {
continue;
}
print!("{i} ");
}
println!();
}
```
```
1 2 3 5 6 7 9 10
```
Multiples of 4 hit the `continue`, skip the print, and the `for` serves the next value. Like the early returns of lesson [7.2](@/chapter-7/working-with-if-and-else.md), a `continue` at the top of a loop body ("skip what I don't want, then proceed with clean cases") often reads better than wrapping the whole body in an `if`.
One trap, inherited from every language with `continue`. In a *while* loop, the update statement lives in the body, and `continue` jumps over the rest of the body:
```rust
fn main() {
let mut count = 0;
while count < 10 {
if count == 5 {
continue; // jumps to the condition check... without the += 1
}
print!("{count} ");
count += 1; // runs forever once count hits 5
}
}
```
This prints `0 1 2 3 4 ` and then sits forever: at 5, the `continue` skips the increment, so the condition re-checks `5 < 10` for eternity. New programmers meet this bug within a week of learning `continue`. Notice, though, what it requires: an update statement that lives *below* the `continue`. A `for` loop has no update statement in the body at all (the range does the advancing), so this entire bug is impossible there. One more point for `for` as your default loop.
{% callout(kind="best", title="Best practice") %}
Use `break` and `continue` when they simplify a loop's logic, and prefer them to bool flag variables and extra nesting. (Some older style guides ban them as "hidden jumps"; modern practice, and this course, disagrees, for the same reason it likes early returns: handling the exceptional case immediately keeps the main path flat.)
{% end %}
## break with a value: loop's exclusive
Here's lesson 7.4's promise, kept. Remember that `loop` is a promise to the compiler ("only a `break` gets me out of this"). The compiler repays it: a `loop` is an *expression*, and `break` can carry the loop's value out with it.
First, the version you'd write without it, using a flag variable:
```rust
fn main() {
let mut found = false;
let mut n = 1;
while !found {
if n * n > 300 {
found = true;
} else {
n += 1;
}
}
println!("first square over 300: {n} squared");
}
```
It works, but `found` is pure bureaucracy (one more mutable variable, one more thing the condition and the body must agree about). Now with `break`-with-value:
```rust
fn main() {
let mut n = 1;
let answer = loop {
if n * n > 300 {
break n;
}
n += 1;
};
println!("first square over 300: {answer} squared");
}
```
```
first square over 300: 18 squared
```
`break n` ends the loop *and* makes `n` the value of the whole `loop` expression, which lands in `answer`. The flag is gone, and the loop now says what it's for: producing a value. (Note the `;` after the closing brace: the `loop` is the right-hand side of a `let` statement, same as any expression.)
Why does only `loop` get this? Try it in a `while` and the compiler explains:
```rust
fn main() {
let mut n = 1;
let answer = while n < 100 {
n *= 2;
break n;
};
}
```
```
error[E0571]: `break` with value from a `while` loop
--> src/main.rs:5:9
|
3 | let answer = while n < 100 {
| ------------- you can't `break` with a value in a `while` loop
4 | n *= 2;
5 | break n;
| ^^^^^^^ can only break with a value inside `loop` or breakable block
|
help: use `break` on its own without a value inside this `while` loop
|
5 - break n;
5 + break;
|
```
The logic: a `while` (or `for`) can end because its condition fails or its range runs dry, paths that pass through no `break` and therefore carry no value. What would `answer` be then? Rather than invent a default, Rust restricts break-with-value to `loop`, where every exit is a `break` and a value is always guaranteed. It's lesson [4.7](@/chapter-4/introduction-to-if-expressions.md)'s missing-else rule wearing a loop costume.
Lesson 7.4's menu now has its final form, and it's the idiom you'll actually see in the wild:
```rust
use std::io;
fn main() {
let choice = loop {
println!("Please pick an option (1-4):");
let mut input = String::new();
io::stdin().read_line(&mut input).expect("failed to read line");
let n: u32 = input.trim().parse().expect("please type a number");
if n >= 1 && n <= 4 {
break n;
}
println!("{n} is not on the menu, try again.");
};
println!("you picked option {choice}");
}
```
```
Please pick an option (1-4):
9
9 is not on the menu, try again.
Please pick an option (1-4):
3
you picked option 3
```
The loop *is* the validation: nothing escapes it but a valid choice, and `choice` doesn't even need to be mutable.
## Loop labels: escaping nested loops
`break` and `continue` act on the innermost loop containing them. What if you're in a nested loop and want out of the *outer* one? This is the one job for which C++ programmers still reach for `goto` (their own textbooks admit as much, through gritted teeth). Rust deleted goto and kept a tool shaped exactly like this problem: name the loop, and tell `break` which name you mean.
```rust
fn main() {
'outer: for x in 1..=3 {
for y in 1..=3 {
if x * y == 6 {
println!("{x} * {y} is 6, calling off the whole search");
break 'outer;
}
println!("{x} * {y} = {}", x * y);
}
}
println!("done");
}
```
```
1 * 1 = 1
1 * 2 = 2
1 * 3 = 3
2 * 1 = 2
2 * 2 = 4
2 * 3 is 6, calling off the whole search
done
```
A **loop label** is a name starting with a single quote, placed before the loop with a colon. `break 'outer` ends the loop named `'outer`, both levels at once. A plain `break` in the same spot would only have ended the inner loop, and the search would have resumed at `x = 3`. `continue` takes labels too: `continue 'outer` abandons the inner loop and starts the outer loop's next iteration.
(That leading quote will look stranger before chapter 17 than after; it's the same marker Rust uses for another kind of name. For now: label names look like `'this`.)
## Quiz time
**Question #1**
What does this print?
```rust
fn main() {
for i in 1..=4 {
if i == 2 {
continue;
}
if i == 4 {
break;
}
print!("{i} ");
}
println!("done");
}
```
Show solution
```
1 3 done
```
1 prints. 2 hits the `continue` and is skipped. 3 prints. 4 hits the `break` before its print, ending the loop. Execution lands on the `println!` after the loop.
**Question #2**
Rewrite this function to eliminate the flag variable, using a `loop` and break-with-value:
```rust
fn first_power_of_two_over(limit: u32) -> u32 {
let mut done = false;
let mut n = 1;
while !done {
if n > limit {
done = true;
} else {
n *= 2;
}
}
n
}
```
Show solution
```rust
fn first_power_of_two_over(limit: u32) -> u32 {
let mut n = 1;
loop {
if n > limit {
break n;
}
n *= 2;
}
}
fn main() {
println!("{}", first_power_of_two_over(1000));
}
```
```
1024
```
The `loop` is the function's tail expression, so the broken-out value is also the return value; `done` and the final bare `n` both disappear. (A `return n` instead of `break n` is also correct here, and a matter of taste.)
**Question #3**
Using nested `for` loops over `1..=9` each, find and print the *first* pair `(a, b)` whose product is divisible by 9, where `a < b`, then stop searching entirely. ("First" means: smallest `a`, then smallest `b`.) Use a labeled break.
Show solution
```rust
fn main() {
'search: for a in 1..=9 {
for b in 1..=9 {
if a < b && (a * b) % 9 == 0 {
println!("{a} * {b} = {}, divisible by 9", a * b);
break 'search;
}
}
}
}
```
```
1 * 9 = 9, divisible by 9
```
The label is doing real work: a plain `break` would end only the inner loop, and the search would continue with `a = 2`, finding (and printing) more pairs. If you'd reached for a `found` flag checked by both loops instead, compare the two versions; the label says "stop the whole search" in one word.
Next: a kind of repetition that uses no loop at all. Functions that call themselves.