2.xChapter 2 summary and quiz

Last updated June 11, 2026

Chapter 2 turned your programs from monologues into teams. The review, then the chapter's rite-of-passage program.

Quick review

A function is a reusable, named piece of program dedicated to one job; you call it with parentheses, execution jumps in, runs the body, and returns to the caller. Definitions can appear in any order in the file (no forward declarations exist, or are needed). Calling without parentheses (greet;) compiles, does nothing, and draws a warning.

A function declares what it yields with a return type after ->, and its body is a block whose tail expression is the returned value; a reflexive semicolon on that last line is the classic E0308 ("expected i32, found ()"), with the compiler's help line naming the cure. The return keyword exists for early exits; code after it is unreachable, and the compiler says so. No arrow means the function returns (). Repeated code extracted into one function is the DRY principle at work.

A parameter is a typed variable in the definition's parentheses; an argument is the value a caller supplies for it. Parameter types are mandatory because the function's signature is a checked, local contract: wrong-type and wrong-count mistakes (E0061) are reported at the call site, with the contract quoted. For this chapter's types, arguments are copied into parameters (the honest asterisk lives in chapter 8).

A variable's scope runs from its let to its block's closing brace; functions' variables (local variables, parameters included) are invisible to each other, which is why names can repeat across functions without a meeting ever occurring. Define variables near first use, and remember blocks-as-expressions can scope scratch work out of existence.

Functions buy organization, reuse, testing, extensibility, and abstraction; one function, one job, named for the job. Design top-down: state the goal, list requirements, decompose into function-sized pieces, then build from a compiling todo!() skeleton, one implemented-and-tested function at a time, simplest version first.

Quiz time

Question #1

Without running it, trace this program's complete output:

fn main() {
    let result = transform(3);
    println!("main: {result}");
}

fn transform(n: i32) -> i32 {
    println!("transform: got {n}");
    let n = n + 1;
    double(n)
}

fn double(n: i32) -> i32 {
    println!("double: got {n}");
    n * 2
}
Show solution
transform: got 3
double: got 4
main: 8

transform receives 3, makes a new n of 4 (block scoping at work), and its tail expression is the call to double, whose own result (8) therefore becomes transform's result too. Three functions, three private ns, no interference.

Question #2

Predict the compiler error:

fn half(n: i32) -> i32 {
    n / 2;
}

fn main() {
    println!("{}", half(10));
}
Show solution

E0308 at half: expected i32, found (), because the semicolon turned the tail expression into a statement. The help line says to remove the semicolon. If this one's reflexive by now, the chapter did its job.

Question #3

The classic. Write a program with three functions:

main should use them to produce, for inputs 6 and 4:

Enter a whole number:
6
Enter a whole number:
4
The sum is: 10
Show solution
fn main() {
    let x = read_number();
    let y = read_number();
    print_answer(add(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")
}

fn add(x: i32, y: i32) -> i32 {
    x + y
}

fn print_answer(answer: i32) {
    println!("The sum is: {answer}");
}

Note how main is four lines that read as the plan, the prompt moved inside read_number (it belongs to that job), and compute (add) stayed separate from output (print_answer), per lesson 2.5.

A historical footnote you've earned: this course's C++ inspiration sets this same exercise, then spends its versions 2 and 3 splitting the program across multiple files with forward declarations and a header guard. The Rust edition of those follow-ups is chapter 13, lesson 13.4, where the whole topic takes one lesson, mostly because there's so little to configure.

That's functions. Chapter 3 takes the other half of the craft: what to do when the program you built out of them doesn't do what you meant.