# 7.x Chapter 7 summary and quiz
> Control flow reviewed: match, the three loops, jumps, recursion, halts, and dependencies, plus a quiz that builds real programs.
Source: https://learnrust.net/chapter-7/chapter-7-summary-and-quiz/
This was the chapter where your programs stopped reading like scripts and started reading like *decisions*: branching, repeating, and ending on their own judgment. It's also the chapter where the course's first complete program shipped. The review, then a quiz with two programs worth keeping.
## Quick review
A program's **execution path** is the sequence of statements it actually runs, and a **straight-line program** has only one. Control flow statements create alternatives: when execution goes somewhere other than straight ahead, it has **branched**. Rust's toolkit: conditionals (`if`, `match`), loops (`loop`, `while`, `for`), jumps (`break`, `continue`, `return`), and halts. What the toolkit deliberately omits (goto, do-while, switch, and exceptions) all reduce to the same trade: delete the bug-prone construct, keep its one good job ([7.1](@/chapter-7/control-flow-introduction.md)).
An `if`/`else if` chain takes its *first* true arm and ignores the rest; separate `if` statements each get evaluated. Early returns flatten functions by handling the rejections first, and logical operators flatten nested conditions. A variable born inside an arm dies at the arm's closing brace; when arms exist to produce a value, the `if`-as-expression delivers it past the braces ([7.2](@/chapter-7/working-with-if-and-else.md)).
A **match** evaluates its **scrutinee** once and tests it against each arm's **pattern**, top to bottom, first fit wins, no fallthrough. `_` matches anything; `|` combines patterns into one arm. A match must be **exhaustive**: the compiler proves your arms cover every possible value (E0004 names exactly what you missed), which means a match doesn't just check the cases you thought of, it checks the completeness of your thinking. Patterns that bind, guard, and destructure arrive with chapter 11 ([7.3](@/chapter-7/introduction-to-match.md)).
A **while** checks before every **iteration** and runs zero times if the condition starts false. `loop` is the deliberate infinite loop (the compiler will tell you to use it if you write `while true`), escaped by `break`, and `loop` plus a conditional `break` replaces do-while. On unsigned counters, `while i >= 0` is always a bug, and rustc labels it ([7.4](@/chapter-7/loop-and-while.md)). A **for** loop walks a sequence with no exposed bookkeeping: `0..n` is **half-open** (n iterations), `0..=n` **inclusive**, `.rev()` reverses, `.step_by(k)` skips; the loop variable is a fresh binding each pass. Default to `for` when the values are known up front, `while` for re-evaluated conditions, `loop` for "until I say" ([7.5](@/chapter-7/for-loops-and-ranges.md)).
**break** ends its loop, **continue** ends its iteration, and both can target an enclosing loop by **label** (`break 'outer`), the civilized replacement for goto's last legitimate job. From a `loop` (only), `break` can carry a value out, making the loop an expression and retiring a whole genre of flag variables ([7.6](@/chapter-7/break-continue-and-loop-labels.md)).
A **recursive** function calls itself, shrinking toward a **base case**; without a reachable one, each unfinished call holds its ledge of memory until the stack runs out (a **stack overflow**). Prefer loops where they're equally clear, which for plain counting they always are; recursion shines when the problem is self-similar, and Rust doesn't promise tail-call optimization ([7.7](@/chapter-7/introduction-to-recursion.md)).
Programs report an **exit code** at the end: 0 for success, nonzero for failure, read by scripts and CI rather than humans. `std::process::exit(code)` halts immediately from anywhere, skipping cleanup and unflushed output; `panic!` crashes on purpose with a message addressed to the programmer, for states that should be impossible (expected failures are chapter 12's `Result` business) ([7.8](@/chapter-7/halting-your-program-early.md)).
A **PRNG** is deterministic scrambling: the **seed** sets its state, and same seed means same sequence, forever. Real randomness-shaped variety comes from OS-seeded generators, via your first **dependency**: `cargo add rand` writes one semver line into `Cargo.toml`, `Cargo.lock` pins exact versions for reproducible builds, and `rand::random_range(1..=100)` (or a reused `rand::rng()` generator) does the rolling ([7.9](@/chapter-7/adding-a-dependency-random-numbers.md)). All of it converged in the guessing game, whose optimal strategy you already knew from debugging: halve the range every guess. Its name is **binary search**, and 2⁷ = 128 is why 7 guesses crack 100 numbers ([7.10](@/chapter-7/project-number-guessing-game.md)).
## Quiz time
**Question #1**
Three quick ones, one sentence each:
a) `loop { ... }` and `while true { ... }` run identically. Why does the compiler (and this course) still insist on `loop`?
b) You're summing user inputs until the user types `0`. Which loop, and why?
c) You're printing a 12-row multiplication table. Which loop, and why?
Show solution
a) `loop` declares there's no exit condition, so the compiler treats it specially: only `loop` can `break` with a value, because every exit from a `loop` is a `break`. (`while true` additionally draws a warning.)
b) A `loop` with a sentinel `break` (or break-with-value): iteration count depends on a decision made *inside* the loop.
c) A `for` over `1..=12`: the values to visit are known up front, and the loop does the counting bookkeeping for you.
**Question #2**
What does this print?
```rust
fn main() {
'outer: for i in 1..=3 {
for j in 1..=3 {
match (i * j) % 4 {
0 => break 'outer,
1 => continue,
_ => print!("{} ", i * j),
}
}
println!("row {i} done");
}
println!("end");
}
```
Show solution
```
2 3 row 1 done
2 end
```
Row 1: products 1, 2, 3; the 1 hits `continue` (skipping its print), 2 and 3 print, the row completes. Row 2: product 2 prints, then product 4 hits `break 'outer`, abandoning both loops at once, so neither "row 2 done" nor row 3 happens; execution lands on the final `println!`. (Note `continue` targeted the *inner* loop, its innermost enclosing one, and that a `match` arm can hold a jump statement just fine.)
**Question #3**
Predict the compiler's reaction to each, precisely:
```rust
// Program A
fn main() {
let on = true;
match on {
true => println!("lights on"),
}
}
```
```rust
// Program B
fn main() {
let result = for i in 1..=10 {
if i * i > 50 {
break i;
}
};
println!("{result}");
}
```
Show solution
Program A: E0004, non-exhaustive patterns: `false` not covered. A `bool` has exactly two values and the match handles one; exhaustiveness is checked against the type, and the fix is a `false` arm (or `_`).
Program B: E0571, `break` with value from a `for` loop. A `for` can end by exhausting its range without ever reaching the `break`, so the loop can't promise a value; only `loop` can. (Rewrite with `let mut`/plain `break`, or a `loop` with its own counter.)
**Question #4**
Chapter 4's falling-ball program ([4.x](@/chapter-4/chapter-4-summary-and-quiz.md), question #3) had a hardcoded flaw: it reported the ball's height at t = 0 through 5 because *we* worked out that a 100-meter drop takes about 5 seconds. Replace the six `report` calls with a loop that keeps reporting, one second at a time, until the ball has landed, for any tower height. (Reuse `ball_height`, `report`, and `read_number` from chapter 4 unchanged.)
Show solution
```rust
fn ball_height(start: f64, seconds: f64) -> f64 {
let fallen = 9.8 * seconds * seconds / 2.0;
let height = start - fallen;
if height < 0.0 { 0.0 } else { height }
}
fn report(start: f64, seconds: f64) {
let height = ball_height(start, seconds);
if height > 0.0 {
println!("At {seconds} seconds, the ball is at height: {height} meters");
} else {
println!("At {seconds} seconds, the ball is on the ground.");
}
}
fn read_number() -> f64 {
let mut input = String::new();
std::io::stdin()
.read_line(&mut input)
.expect("failed to read input");
input.trim().parse().expect("that wasn't a number")
}
fn main() {
println!("Enter the height of the tower in meters:");
let start = read_number();
let mut seconds = 0.0;
loop {
report(start, seconds);
if ball_height(start, seconds) <= 0.0 {
break;
}
seconds += 1.0;
}
}
```
```
Enter the height of the tower in meters:
100
At 0 seconds, the ball is at height: 100 meters
At 1 seconds, the ball is at height: 95.1 meters
At 2 seconds, the ball is at height: 80.4 meters
At 3 seconds, the ball is at height: 55.9 meters
At 4 seconds, the ball is at height: 21.599999999999994 meters
At 5 seconds, the ball is on the ground.
```
Same output as chapter 4's version for a 100-meter tower, but try 500, or 2: the program now *notices* the landing instead of being told when it is, which is this chapter's whole thesis in one diff. The loop is a `loop`-with-`break` rather than a `while`, so the landing line itself still prints (check the condition *after* reporting, not before); a `while ball_height(...) > 0.0` would stop one line early. Why not a `for`? Because the number of iterations is exactly what we don't know anymore. (The digit eruption at t = 4 is still chapter 4's rounding error; chapter 5 gave you `{:.1}` if it offends you.)
**Question #5**
Write `is_prime(x: u32) -> bool` (a prime is a number ≥ 2 divisible only by 1 and itself), then use it to print every prime up to 30. Checking divisibility by every number from 2 to x−1 is fine for full credit. Extra credit: it's enough to check odd divisors whose *square* doesn't exceed x (any divisor pair has one member at or below the square root). Use `test * test <= x` so you don't need a square-root function.
Show solution
Full credit:
```rust
fn is_prime(x: u32) -> bool {
if x < 2 {
return false;
}
for divisor in 2..x {
if x % divisor == 0 {
return false;
}
}
true
}
fn main() {
for n in 1..=30 {
if is_prime(n) {
print!("{n} ");
}
}
println!();
}
```
```
2 3 5 7 11 13 17 19 23 29
```
Early-return style at work: each divisor found is an immediate `false`, and surviving the whole loop *is* the proof of primality (note 2 survives trivially: the range `2..2` is empty). Extra credit, same `main`:
```rust
fn is_prime(x: u32) -> bool {
if x < 2 {
return false;
}
if x % 2 == 0 {
return x == 2;
}
let mut test = 3;
while test * test <= x {
if x % test == 0 {
return false;
}
test += 2;
}
true
}
```
Evens are dispatched in one line (only 2 among them is prime), then only odd divisors are tried, and only while `test * test <= x`. For x = 29: tests 3 and 5, done; the naive version tried 27 divisors. The square trick matters more than it looks: for a ten-digit number, it's the difference between ~50,000 checks and ~5,000,000,000. A `while` loop, note, because the stopping condition (`test * test <= x`) isn't a simple range of values.
That's control flow: your programs now branch, repeat, recurse, quit early, and roll dice. Every value in them still obeys one unexamined rule, though: it lives exactly as long as its scope and vanishes at a closing brace. Chapter 8 finally asks the question this course exists to answer gently: who *owns* the data, and what does it mean to give it away? See you there.