---
title: "4.x Chapter 4 summary and quiz"
description: "Every type and term from Chapter 4, plus a calculator program and the famous falling-ball exercise."
url: "https://learnrust.net/chapter-4/chapter-4-summary-and-quiz/"
last_updated: "2026-06-11"
---

# 4.x Chapter 4 summary and quiz

This was the chapter where Rust's type system stopped being scenery. The review, then two programs that need most of it.

## Quick review

Memory is **bits** in 8-bit, addressable **bytes**; a **data type** fixes how many bytes a value uses and what the bits mean, and n bits afford 2ⁿ values. Sizes are exact and in the names: ten integer types from `i8`/`u8` to `i128`/`u128` (**signed** `i` for possibly-negative, **unsigned** `u` for never-negative), plus pointer-sized `usize`/`isize`, which exist to measure memory and arrive on their own with collections. Default to `i32`; deviate with a reason; constants like `i32::MAX` know the edges. Different numeric types never mix implicitly, which converts the classic signed/unsigned disasters into compile errors.

Integer literals take suffixes (`42u8`), underscores, and bases (`0xFF`, `0o377`, `0b1010`); out-of-range literals are rejected at compile time, range quoted. Runtime **integer overflow** is *defined*: dev builds panic at the guilty operation, release builds wrap like an odometer, and the `checked_`/`wrapping_`/`saturating_` families make the policy explicit when the edge is part of the job.

**Floating-point** `f32`/`f64` (default and recommendation: `f64`) hold approximations: ~7 versus ~15-16 trustworthy digits, binary fractions that can't quite say 0.1, rounding that accumulates (ten dimes ≠ one dollar), so never compare floats with `==` (tolerance technique in 6.4). Division by zero yields `inf`/`-inf`/`NaN` rather than a panic, and NaN equals nothing, itself included.

**bool** is `true`/`false`, one byte, flipped by `!`, born mostly from comparisons (`==`, `!=`, `<`, `>`, `<=`, `>=`), and there is **no truthiness**: integers don't pass for bools, and `if count != 0` says what `if (count)` only implied. **if** demands a bool condition, skips the parentheses, insists on braces, chains with `else if`, and is an *expression*: `let label = if c { a } else { b }`, arms agreeing on type, `else` mandatory in value position. The ternary operator sends its regrets. And `if x = 5` is a compile error here, paying off lesson 1.4's oldest warning.

**char** is four bytes, one Unicode scalar value, single-quoted, emoji included; `'5'` is catalog entry 53, not the number 5. **Type inference** types your locals from values *and* usage, whole function body at a time, falling back to `i32`/`f64`, asking ("type annotations needed") when evidence runs out; signatures stay explicit by design. Conversions are explicit with **as**: widening exact, integer-narrowing truncates bits, float-to-int truncates then *saturates*, int-to-float can round, and none of it warns, because the keyword is your signature. **Tuples** group fixed, possibly-mixed values (`(i32, f64)`), unpack by `.0`/`.1` or **destructuring** (`let (x, y) = ...`), give functions multiple return values, and at length zero turn out to *be* the unit type `()` you've known since lesson 1.11.

## Quiz time

**Question #1**

Pick the most appropriate type, one phrase of justification each:

a) the number of students in a school
b) whether a checkbox is ticked
c) a bank account balance in *cents*, possibly overdrawn, possibly enormous
d) the percentage of a download completed, shown as 73.4%
e) the key a player pressed, like `'w'`

<details class="solution">
<summary>Show solution</summary>

a) `i32`: the default; school sizes don't strain it.
b) `bool`: it *is* a yes/no.
c) `i64`: signed because overdrawn happens, 64-bit because enormous happens, and cents-as-integers because lesson [4.5](https://learnrust.net/chapter-4/floating-point-types/) told you what floats do to money.
d) `f64`: fractional by nature, and the default float.
e) `char`: one character is what it is.

</details>

**Question #2**

Write the chapter's calculator. It reads two numbers (allow decimals), then an operator (`+`, `-`, `*`, or `/`), and prints the result in the format shown; for anything else it admits defeat politely. A sample run:

```
Enter a number:
8
Enter another number:
2
Enter an operator (+, -, *, or /):
*
8 * 2 is 16
```

Hints: the lesson [1.12](https://learnrust.net/chapter-1/developing-your-first-program/) recipe reads an `f64` if you change one annotation; read the operator as text and compare it (after `trim`) against `"+"` and friends with `==`; an `else` chain dispatches; lesson [2.6](https://learnrust.net/chapter-2/designing-your-first-programs/)'s decomposition advice applies.

<details class="solution">
<summary>Show solution</summary>

```rust
fn main() {
    let a = read_number();
    let b = read_number();

    println!("Enter an operator (+, -, *, or /):");
    let mut op_input = String::new();
    std::io::stdin()
        .read_line(&mut op_input)
        .expect("failed to read input");
    let op = op_input.trim();

    if op == "+" {
        println!("{} + {} is {}", a, b, a + b);
    } else if op == "-" {
        println!("{} - {} is {}", a, b, a - b);
    } else if op == "*" {
        println!("{} * {} is {}", a, b, a * b);
    } else if op == "/" {
        println!("{} / {} is {}", a, b, a / b);
    } else {
        println!("'{op}' isn't an operator I know.");
    }
}

fn read_number() -> f64 {
    println!("Enter a 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 number")
}
```

Things worth checking against your version: the recipe's annotation became `f64` (via the return type, lesson [4.9](https://learnrust.net/chapter-4/type-inference-and-annotations/)'s evidence rules at work); the operator is compared as trimmed text; the final `else` handles the unexpected, per lesson [3.6](https://learnrust.net/chapter-3/finding-issues-early/)'s defensive habit. Slightly different prompts or formats: fine. (Two honest limitations you now know enough to *name*: dividing by zero produces `inf` since these are floats, and typing `potato` as a number still panics via `expect`. Chapter 12 civilizes both.)

</details>

**Question #3 (extra credit, the famous one)**

A ball drops from a tower. The user enters the tower's height in meters; gravity is 9.8 m/s², and after t seconds the ball has fallen 9.8 · t² / 2 meters. Print the ball's height at t = 0 through 5, clamping at the ground:

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

(Yes, the t = 4 line really looks like that, and you should reproduce it as-is. Three lines print tidily and one erupts in digits: that's lesson [4.5](https://learnrust.net/chapter-4/floating-point-types/)'s rounding error making a personal appearance, since 78.4 has no exact binary spelling. Chapter 5's formatting tools will let you print it to one decimal like a civilized program.)

<details class="solution">
<summary>Show solution</summary>

```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 main() {
    println!("Enter the height of the tower in meters:");
    let start = read_number();
    report(start, 0.0);
    report(start, 1.0);
    report(start, 2.0);
    report(start, 3.0);
    report(start, 4.0);
    report(start, 5.0);
}

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")
}
```

Design notes: `ball_height` computes and clamps (an `if` expression as the tail), `report` decides how to *say* it (compute and print kept separate per lesson [2.5](https://learnrust.net/chapter-2/why-functions-are-useful/), then introduced by a function that owns the phrasing). If your t = 4 line surprised you with its digit eruption, reread the question's parenthetical and feel the chapter close its own loop. And grammar pedants are invited to enjoy "At 1 seconds" until chapter 5's formatting tools offer a fix for that, too.

And those six nearly identical `report` lines in `main`? You're *supposed* to find them irritating. The tool that collapses them into two lines is the `for` loop, chapter 7, and you've just motivated it personally.

</details>

That's milestone territory: types, decisions, and real little programs. Chapter 5 starts the data story proper, with constants, shadowing, and the long-promised truth about strings.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
