# 3.x Chapter 3 summary and quiz > Debugging terms in review, plus a quiz of real broken programs to hunt through. Source: https://learnrust.net/chapter-3/chapter-3-summary-and-quiz/ Chapter 3 in one breath, then the quiz, which for this chapter means: here are some broken programs, go be the process. ## Quick review A **syntax error** breaks the grammar and dies at compile time. A **semantic error** (**logic error**) compiles and does the wrong thing. Rust reclassifies much of the traditional semantic-error category into compile-time errors, and catches another slice at runtime as a **panic**: an immediate, honest stop with a file-and-line report (dividing by a zero variable, a failing `expect`). What remains is the silent wrong answer, which is yours. Debugging follows six steps: reproduce, locate, understand, plan, fix, retest (including the neighbors). Reproduce *before* theorizing; fix only what you understand; a vanished symptom is evidence, not proof. Locating is search-space management. Inspect what's small and recent (the bug is usually in the last thing you touched); when staring fails, switch to running experiments: check values at the *seams* between functions, halve the territory with each observation, comment chunks out to test involvement, and change one thing per experiment, undoing as you go. Programs have two output streams: stdout (`println!`, the product) and stderr (`eprintln!`, the commentary), and redirection (`>`) separates them. The **dbg!** macro is purpose-built instrumentation: it prints `[file:line:column] expression = value` to stderr and *returns the value*, so it wraps any expression in place. Probes are temporary; sweep them before calling work done. A **debugger** pauses a live program: **breakpoints** choose where, **stepping** (over, into, out) moves in controlled doses, **watching** reads variables, and the **call stack** shows the chain of calls in progress, top to `main`. Use the dev profile, since release builds shed the debug info and rearrange the code. And the prevention habits, cheaper than any hunt: build in small compiled steps, keep functions small and single-jobbed, never mix cleanup with behavior changes, ask what the worst caller could do, heed warnings and Clippy, and (chapter 14 trailer) write what "correct" means as runnable `#[test]` functions. ## Quiz time **Question #1** A user reports this program always answers `0` plus the second number, whatever they type first. Find the bug, name which step-2 evidence would have exposed it fastest, and fix it. ```rust fn main() { let x = 0; read_number(); let y = read_number(); println!("{} + {} is {}", x, y, x + y); } fn read_number() -> i32 { println!("Enter a whole number:"); 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 whole number") } ```
Show solution The first `read_number()` call's return value is *discarded*: the statement runs the prompt-and-read, then throws the result away, and `x` stays the 0 it was initialized to. The fix: ```rust let x = read_number(); let y = read_number(); ``` Fastest evidence: `dbg!(x)` (or a breakpoint) just before the `println!`, showing `x = 0` no matter what was typed; the seam check immediately clears `read_number`'s internals and points at the wiring in `main`. (The program even prompts twice and uses only one answer, a symptom visible with no tools at all: it *asks*, then ignores you, like a waiter.)
**Question #2** This one compiles with a warning and then dies at runtime. Explain the crash, find the bug, and say what the warning was trying to tell you. ```rust fn main() { let mut x = read_number(); let y = 0; x = read_number(); println!("{} / {} is {}", x, y, x / y); } fn read_number() -> i32 { println!("Enter a whole number:"); 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 whole number") } ``` ``` Enter a whole number: 8 Enter a whole number: 2 thread 'main' (1127017) panicked at src/main.rs:5:37: attempt to divide by zero ```
Show solution The second input was *meant* to become `y`, but the line assigns it to `x` again (a copy-paste classic). `y` keeps its placeholder 0, and the division panics. The fix is the intended wiring, which also dissolves the `mut` and the placeholder: ```rust let x = read_number(); let y = read_number(); ``` The warning ("value assigned to `x` is never read", on the *first* read) was the whole story in advance: a value was computed, stored, and overwritten before any use, which is precisely the bug. Warnings first, then tools.
**Question #3** No code to run for this one; predict from your mental model. The program below is paused by a breakpoint on the marked line. What does the call stack panel show, top to bottom? ```rust fn main() { outer(); } fn outer() { helper(); } fn helper() { let x = 1; // breakpoint here println!("{x}"); } ```
Show solution Top to bottom: `helper`, `outer`, `main`. The top entry is where execution is paused; each entry below is a function mid-call, waiting for the one above it to return; `main` anchors the bottom of every Rust stack.
That's the on-ramp milestone complete: tools installed, language basics, functions, and now debugging. Chapter 4 starts the language's real depth: what those `i32`s have been all along, the rest of their family, and the first appearance of `if`. The programs get noticeably more interesting from here.