---
title: "5.x Chapter 5 summary and quiz"
description: "Constants, shadowing, String and &str, formatting and parsing reviewed, plus a quiz that uses the whole chapter at once."
url: "https://learnrust.net/chapter-5/chapter-5-summary-and-quiz/"
last_updated: "2026-06-12"
---

# 5.x Chapter 5 summary and quiz

This was the chapter where text stopped being a prop and became a subject, and where two ideas (constants and shadowing) joined the permanent toolkit. The review, a bug hunt, and a program that needs all of it.

## Quick review

A **constant** is declared with `const`, requires a type annotation, lives in any scope including global, and must have a value the compiler can settle *at compile time*: literals and arithmetic on them yes, anything runtime (user input, ordinary variables) no, enforced by `E0435`. Constants are named in `SCREAMING_SNAKE_CASE`, and the compiler warns you personally if you forget. They're the cure for **magic numbers**: bare literals whose meaning and update-sites nobody can reconstruct later. Name what isn't obvious; leave obvious conversion factors alone. A `static` is a fixed value with one address for the whole program; until an address matters (chapters 21 and 25), prefer `const`.

**Shadowing** is a second `let` reusing a name: a new variable steps in front of the old, no mutation occurs, no `mut` is needed, and the old one becomes unreachable rather than changed. A shadow ends with its scope (block shadows don't leak out, unlike real assignment), and unlike a `mut` rebind, a shadow may change *type*, which is the trick behind the course's favorite line: `let age: u32 = age.trim().parse().expect(...)`, one concept keeping one name across three types.

Text comes in two flavors with one relationship. A **String** owns its text: growable (`push_str` for text, `push` for a single-quoted `char`), runtime-sized, created via `String::new()`, `String::from(...)`, or `.to_string()`. A **&str** is a borrowed *view* of text owned elsewhere: every string literal is one, and so is `trim`'s return value, a copy-free window into the `String` it came from (the owner/viewer split runs on chapter 9's machinery; views that outlive their text don't compile at all, and chapter 9 explains why). Store text as `String`, accept parameters as `&str`, lend with `&` at the call site. And `len()` counts *bytes*, not characters: `"café"` is 4 characters, 5 bytes.

**format!** is `println!` returning the `String` instead of printing it, and both speak the brace mini-language: width `{:8}`, alignment `{:<}` `{:>}` `{:^}` (fill character optional: `{:->8}`), float precision `{:.2}` (display-side rounding only), zero-padding `{:08}`, forced sign `{:+}`, all combinable as in `{total:>7.2}`. **{:?}** asks for the **debug** view, made for programmers: tuples print, strings wear their quotes and show their `\n`s, and `dbg!` has been using it all along. **parse** crosses the text/number border deliberately: it targets the type named by an annotation or by the **turbofish** (`parse::<i32>()`), accepts only a clean trimmed number, and reports failure (`InvalidDigit`, `Empty`) through the value that `expect` unwraps-or-panics, properly handled in chapter 12.

## Quiz time

**Question #1**

Three claims, true or false, one sentence of why:

a) `const` and an immutable `let` are interchangeable for a value that never changes.
b) After `let x = "5"; let x = 5;` the program has mutated `x`.
c) A function parameter for read-only text should usually be `String`.

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

a) False: `const` additionally requires the value to be known at compile time, so runtime-but-frozen values (user input) can only be `let`.
b) False: nothing was mutated; two variables existed, the second (a different type, which `mut` could never allow) now shadows the first.
c) False: `&str` is the read-only text parameter, taking literals and lent Strings alike; `String` parameters are for functions that *keep* the text (chapter 8 business).

</details>

**Question #2**

What does this print?

```rust
fn main() {
    let width = 4;
    {
        let width = width * 2;
        println!("inner: {width}");
    }
    println!("outer: {width}");
    println!("[{:^6}]", width);
}
```

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

```
inner: 8
outer: 4
[  4   ]
```

The block's shadow (computed from the outer value, then standing in front of it) vanishes with the block. Centering one character in six leaves five spaces, split two left, three right: the extra space goes right.

</details>

**Question #3**

This program compiles and works, but it contains three things this chapter taught you to fix. Find them and rewrite it:

```rust
fn shipping_label(name: String) {
    println!("Ship to: {name}");
}

fn main() {
    let mut input = String::new();
    println!("Customer name?");
    std::io::stdin()
        .read_line(&mut input)
        .expect("failed to read input");

    let cleaned = input.trim();
    shipping_label(cleaned.to_string());

    println!("Insurance: {}", 25.0 * 0.012);
}
```

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

The three fixes: `shipping_label` only *reads* the name, so its parameter should be `&str` (which also deletes the awkward `.to_string()` at the call site); the raw/cleaned pair is shadowing's exact use case, retiring the still-reachable untrimmed `input`; and `0.012` (and arguably `25.0`) are magic numbers, meaningless to the next reader and unfindable at update time.

```rust
const INSURANCE_RATE: f64 = 0.012;
const ORDER_VALUE: f64 = 25.0;

fn shipping_label(name: &str) {
    println!("Ship to: {name}");
}

fn main() {
    let mut input = String::new();
    println!("Customer name?");
    std::io::stdin()
        .read_line(&mut input)
        .expect("failed to read input");

    let input = input.trim();
    shipping_label(input);

    println!("Insurance: {}", ORDER_VALUE * INSURANCE_RATE);
}
```

</details>

**Question #4**

The chapter's program. Ask for two people's names and ages, then announce who's older, formatted exactly like the sample run (note the aligned table, two-wide age column). Decompose: a function that reads a name, a function that reads an age, and a function that takes both people and does the announcing (think about its parameter types). If the ages are equal, say so.

```
First person's name?
Ada
First person's age?
36
Second person's name?
Grace
Second person's age?
45
   Ada 36
 Grace 45
Grace is older than Ada.
```

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

```rust
use std::io;

fn read_name(prompt: &str) -> String {
    println!("{prompt}");
    let mut name = String::new();
    io::stdin()
        .read_line(&mut name)
        .expect("failed to read input");
    name.trim().to_string()
}

fn read_age(prompt: &str) -> u32 {
    println!("{prompt}");
    let mut age = String::new();
    io::stdin()
        .read_line(&mut age)
        .expect("failed to read input");
    age.trim().parse().expect("that wasn't a whole number")
}

fn announce(name_a: &str, age_a: u32, name_b: &str, age_b: u32) {
    println!("{name_a:>6} {age_a:>2}");
    println!("{name_b:>6} {age_b:>2}");
    if age_a > age_b {
        println!("{name_a} is older than {name_b}.");
    } else if age_b > age_a {
        println!("{name_b} is older than {name_a}.");
    } else {
        println!("{name_a} and {name_b} are the same age.");
    }
}

fn main() {
    let first_name = read_name("First person's name?");
    let first_age = read_age("First person's age?");
    let second_name = read_name("Second person's name?");
    let second_age = read_age("Second person's age?");

    announce(&first_name, first_age, &second_name, second_age);
}
```

Design notes, chapter by chapter: `read_name` returns an owned `String` (the caller is keeping the text, so a view of the function's local input wouldn't survive; that intuition becomes a compiler-enforced law in chapter 9), while every *parameter* that reads text is a `&str`, lent with `&` at the call site. `read_age` is the 1.12 recipe in its lesson 5.6 form. The table comes from `{:>6}` and `{:>2}`, and the `else if` chain handles the tie the question demanded. (The near-duplicate `read_name`/`read_age` pair should itch slightly; generics, chapter 15, are the scratch.)

</details>

**Question #5**

In your Question #4 solution, could `first_age` be declared `const FIRST_AGE: u32` instead? Why or why not?

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

No. A `const` needs its value at *compile time*, and `first_age` doesn't exist until a user types it at runtime; the compiler would refuse with `E0435` (attempt to use a non-constant value in a constant). Immutable `let` is exactly the right tool: frozen after creation, but created from the running world. That's the chapter's first lesson and its last word.

</details>

Next chapter: the operators you've been using on credit since chapter 1 (`+`, `==`, `%`, and the gang) get their formal introductions, their precedence table, and their edge cases. Bring the floats; 4.5's `==` warning finally gets its proper tool.

## Sitemap

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