---
title: "18.x Chapter 18 summary and quiz"
description: "Review of Vec, String as UTF-8, HashMap, arrays and slices, and choosing a collection, with a chapter quiz and a capstone program."
url: "https://learnrust.net/chapter-18/chapter-18-summary-and-quiz/"
last_updated: "2026-06-13"
---

# 18.x Chapter 18 summary and quiz

The practical payoff chapter: the data structures you'll use in every program. Review, then a capstone that combines several.

## Quick review

A **collection** is an owning handle to a variable amount of heap data ([18.1](https://learnrust.net/chapter-18/introduction-to-collections/)), so it follows every ownership rule from chapter 8. **`Vec<T>`** is the growable list: build with `vec![]` or `Vec::new`, add with `push`, remove with `pop` (returns `Option`) ([18.2](https://learnrust.net/chapter-18/vec-creating-and-updating/)). Access with `[]` (panics out of bounds, for known-good indices) or `get` (returns `Option`, for indices that might be invalid), the chapter-12 panic-versus-recover judgment applied to indexing. Iterate with `for x in &vec` (read), `&mut vec` (modify), or `vec` (consume) ([18.3](https://learnrust.net/chapter-18/vec-iterating-and-borrowing/)); modifying a vector while iterating is an E0502 borrow error, the chapter-9 rule catching a real bug. A vector tracks **length** (elements held) and **capacity** (room allocated); `push` reallocates a doubled buffer when full, and `with_capacity` pre-allocates when the size is known ([18.4](https://learnrust.net/chapter-18/vec-capacity-and-growth/)).

**`String`** is a UTF-8 byte buffer ([18.5](https://learnrust.net/chapter-18/string-as-a-collection/)): `len()` counts bytes, `s[0]` doesn't compile (no good byte-or-char answer), `chars()` yields characters and `bytes()` yields bytes, and slicing panics if a byte index splits a character. **`HashMap<K, V>`** stores key-value pairs ([18.6](https://learnrust.net/chapter-18/hashmap/)): `insert`, `get` (returns `Option`), and the `entry(k).or_insert(d)` idiom for update-or-insert; owned keys/values are moved in. **Arrays** `[T; N]` are fixed-length and stack-allocated, while **slices** `&[T]` are the universal view that accepts both arrays and vectors, so functions should take `&[T]` ([18.7](https://learnrust.net/chapter-18/arrays-and-slices-revisited/)). And the wider family ([18.8](https://learnrust.net/chapter-18/choosing-a-collection/)): `VecDeque` for both-ends access, `HashSet` for uniqueness, `BTreeMap` for sorted keys; default to `Vec` and `HashMap`.

## Quiz time

**Question #1**

When should you use `vec.get(i)` versus `vec[i]`?

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

Use `vec[i]` when the index is known to be valid and an out-of-bounds access would be a bug that should panic. Use `vec.get(i)` (returns `Option<&T>`) when the index might legitimately be out of range (user-supplied, probing), so you can handle `None` gracefully instead of crashing. Both are bounds-checked; the choice is whether out-of-range is a bug or an expected case.

</details>

**Question #2**

For `"naïve"` (where `ï` is two bytes), what do `.len()`, `.chars().count()`, and `&s[0..2]` produce?

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

`.len()` is 6 (bytes: n, a, ï=2 bytes, v, e). `.chars().count()` is 5 (the five characters). `&s[0..2]` is `"na"`, valid because byte 2 is a char boundary (after `n` and `a`, before `ï`). Slicing `&s[0..3]` would *panic*, because byte 3 falls inside the two-byte `ï`. (Bytes versus characters, lesson [18.5](https://learnrust.net/chapter-18/string-as-a-collection/).)

</details>

**Question #3**

Why does the compiler reject pushing to a vector inside a `for x in &vec` loop?

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

The loop holds an immutable borrow of the vector for its whole duration, and `push` needs a mutable borrow, which can't coexist (E0502, lesson [9.3](https://learnrust.net/chapter-9/the-borrow-checker-is-your-friend/)). This prevents a real bug: `push` may reallocate the buffer (lesson [18.4](https://learnrust.net/chapter-18/vec-capacity-and-growth/)), invalidating the loop's reference. The fix is `retain`, collecting changes for after the loop, or iterating indices.

</details>

**Question #4**

The capstone. Write a program that reads a sentence and reports word statistics. Given the string `"the cat sat on the mat the cat"`, produce: the total word count, and a count of how many times each word appears. Use `split_whitespace` to get the words, a `HashMap<&str, i32>` with the `entry` API for the counts, and print the total plus each word's count.

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

```rust
use std::collections::HashMap;

fn word_counts(text: &str) -> (usize, HashMap<&str, i32>) {
    let mut counts: HashMap<&str, i32> = HashMap::new();
    let mut total = 0;

    for word in text.split_whitespace() {
        total += 1;
        *counts.entry(word).or_insert(0) += 1;
    }

    (total, counts)
}

fn main() {
    let text = "the cat sat on the mat the cat";
    let (total, counts) = word_counts(text);

    println!("total words: {total}");
    for (word, count) in &counts {
        println!("{word}: {count}");
    }
}
```

```
total words: 8
the: 3
cat: 2
sat: 1
on: 1
mat: 1
```

(The per-word lines appear in an unspecified order, since `HashMap` iteration order isn't defined, lesson [18.6](https://learnrust.net/chapter-18/hashmap/); only the counts are meaningful.) Design notes. `split_whitespace` yields `&str` views into `text`, so the map keys are `&str` (borrowing from `text`, which outlives the function call). `*counts.entry(word).or_insert(0) += 1` is the update-or-insert idiom: insert 0 for a new word, then increment through the returned mutable reference. The function returns a tuple (lesson [4.11](https://learnrust.net/chapter-4/tuples-and-the-unit-type/)) of the total and the map, destructured in `main`. This small program uses a `HashMap`, the `entry` API, slices as keys, and iteration, the chapter's core in one place.

</details>

You've now got the core type system (chapters 8 to 17) and the everyday data structures (this chapter). You've been writing `for` loops over collections throughout, and you may have noticed methods like `split_whitespace`, `chars`, and `retain` that feel like they're doing something more powerful underneath. Chapter 19 reveals it: **closures and iterators**, where `map`, `filter`, and `collect` turn loops into expressive, lazy pipelines, and the language's functional heart finally shows itself.

## Sitemap

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