# 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.