---
title: "19.x Chapter 19 summary and quiz"
description: "Review of closures, the Fn traits, the Iterator trait, adapters, consumers, and the for-loop desugaring, with a capstone exercise."
url: "https://learnrust.net/chapter-19/chapter-19-summary-and-quiz/"
last_updated: "2026-06-13"
---

# 19.x Chapter 19 summary and quiz

This chapter handed you the tools that make day-to-day Rust expressive. Review, then a capstone that uses most of them at once.

## Quick review

A **closure** is a function written inline that can **capture** variables from its surrounding scope ([19.1](https://learnrust.net/chapter-19/closures/)). It captures by the lightest means that compiles, `&` to read, `&mut` to modify, or by value, and `move` forces every capture to be owned (needed when the closure must outlive the originals). A closure's parameter and return types are inferred from use, then fixed.

Each closure has a unique anonymous type, so you name closures in signatures through the **`Fn` traits** ([19.2](https://learnrust.net/chapter-19/closures-and-the-fn-traits/)): `Fn` (reads captures, callable repeatedly), `FnMut` (mutates captures), `FnOnce` (may consume captures, callable once). They nest `Fn` ⊂ `FnMut` ⊂ `FnOnce`; a parameter should ask for the most permissive bound it needs. Take a closure with `impl Fn(...) -> ...` and return one with `impl Fn` plus `move`. Non-capturing closures and named functions also fit the `fn` pointer type.

An **iterator** is anything implementing `Iterator`: one required method `next(&mut self) -> Option<Item>`, returning `Some` per value and `None` when done ([19.3](https://learnrust.net/chapter-19/the-iterator-trait/)). Get one from a collection with `iter()` (`&T`), `iter_mut()` (`&mut T`), or `into_iter()` (`T`), the `&`/`&mut`/owned choice again. Iterators are **lazy**: they do nothing until consumed.

**Adapters** transform one iterator into another and are lazy and chainable ([19.4](https://learnrust.net/chapter-19/iterator-adapters/)): `map`, `filter`, `enumerate`, `zip`, `rev`, `chain`. **Consumers** drive the iterator to a result ([19.5](https://learnrust.net/chapter-19/consuming-iterators/)): `collect` (needs a target type, via annotation or turbofish), `sum`, `count`, `fold` (the general reduction), and the short-circuiting `find`, `any`, `all`. A **`for` loop** is sugar for `IntoIterator::into_iter` plus `while let Some(x) = iter.next()` ([19.6](https://learnrust.net/chapter-19/for-loops-demystified/)), which is why `for x in &v` was `v.iter()` all along. Pipelines are a **zero-cost abstraction**, identical to hand-written loops in release builds ([19.7](https://learnrust.net/chapter-19/loops-vs-iterators/)); choose the style that reads clearest.

## Quiz time

**Question #1**

When a closure captures a variable, how does the compiler decide whether to borrow it (`&`), borrow it mutably (`&mut`), or move it, and what does `move` override?

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

The compiler picks the least invasive capture the body requires: `&` if it only reads the value, `&mut` if it modifies it, by value only if it must own it. `move` overrides this to force *every* capture to be by value (owned), regardless of how the body uses it, which you need when the closure outlives the originals (returned from a function, sent to a thread).

</details>

**Question #2**

Why must you specify a type for `collect` but rarely for `count`?

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

`collect` can produce many different collection types from the same iterator (`Vec`, `String`, `HashMap`, ...), so the compiler needs you to name the target via a binding annotation or a turbofish (`.collect::<Vec<_>>()`). `count` always returns `usize`, so there's nothing to specify. It's the same missing-type situation as `parse` in lesson 5.6.

</details>

**Question #3**

Predict the output:

```rust
let v = vec![1, 2, 3, 4];
v.iter().filter(|x| **x > 1).map(|x| x * 10);
println!("done");
```

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

It prints only `done`. The adapter chain is never consumed, so it does nothing, no filtering, no mapping (the compiler also warns the iterator is unused). Adapters are lazy; without a consumer like `collect`, `sum`, or a `for` loop, the pipeline is inert. (The `**x` peels `filter`'s `&` off `iter`'s `&i32`.)

</details>

**Question #4**

Which `Fn` trait does each closure implement: `|x| x + 1`, `|x| { vec.push(x) }` (where `vec` is captured), and `move |x| consume(captured_string, x)`?

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

`|x| x + 1` captures nothing and only computes: `Fn` (and therefore also `FnMut` and `FnOnce`). `|x| vec.push(x)` mutates the captured `vec`: `FnMut` (and `FnOnce`), but not `Fn`. `move |x| consume(captured_string, x)`, if `consume` takes `captured_string` by value, moves the captured `String` out when called, so it can run only once: `FnOnce` only.

</details>

## Chapter capstone

Put the chapter to work. Given a block of text, produce a word-frequency report: the five most common words, lowercased, with their counts, ignoring punctuation. This touches closures, adapters, a consumer building a `HashMap`, and a small sort.

Write a program that, for the input below, prints each of the top five words and its count, most frequent first.

```rust
fn main() {
    let text = "the cat sat on the mat. The dog sat on the log. \
                the cat and the dog sat.";

    // your code here
}
```

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

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

fn main() {
    let text = "the cat sat on the mat. The dog sat on the log. \
                the cat and the dog sat.";

    // Split into words, lowercase, strip non-alphabetic edges, count.
    let mut counts: HashMap<String, u32> = HashMap::new();
    for word in text.split_whitespace() {
        let cleaned: String = word
            .chars()
            .filter(|c| c.is_alphabetic())
            .collect::<String>()
            .to_lowercase();
        if !cleaned.is_empty() {
            *counts.entry(cleaned).or_insert(0) += 1;
        }
    }

    // Sort entries by count, descending, and take the top five.
    let mut ranked: Vec<(String, u32)> = counts.into_iter().collect();
    ranked.sort_by(|a, b| b.1.cmp(&a.1));

    for (word, n) in ranked.iter().take(5) {
        println!("{word}: {n}");
    }
}
```

```
the: 5
sat: 3
cat: 2
on: 2
dog: 2
```

(Words tied on count may appear in any order, since `HashMap` iteration order isn't fixed; `the` and `sat` are unambiguous, the rest are a three-way tie at 2.) The pieces: `split_whitespace` yields each word; a `filter`/`collect` pipeline strips punctuation (`chars().filter(is_alphabetic).collect::<String>()`); `entry(..).or_insert(0)` is the chapter-18 count-up idiom; `into_iter().collect()` turns the map into a sortable `Vec`; `sort_by` with a comparison closure orders by count descending (`b.1.cmp(&a.1)`); and `take(5)` is an adapter limiting the output. The cleaning could also be a `map` in a fully iterator-style version, the `for` loop here is a fine choice because the body does a side-effecting `HashMap` update, exactly the "procedure, not transformation" case from lesson 19.7.

</details>

You can now move data through expressive pipelines and bottle up behavior as values. Chapter 20 turns all of this outward: it builds a real command-line program, reading arguments and files and standard input, and the iterator and closure tools you just learned are exactly what make its text-search project read so cleanly.

## Sitemap

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