---
title: "16.x Chapter 16 summary and quiz"
description: "Review of traits, default methods, bounds, impl Trait, deriving, Display, operators, From/Into, trait objects, and composition, with a chapter quiz and a capstone."
url: "https://learnrust.net/chapter-16/chapter-16-summary-and-quiz/"
last_updated: "2026-06-13"
---

# 16.x Chapter 16 summary and quiz

The structural centerpiece of the language, in eleven lessons. Review, then a capstone that uses most of it.

## Quick review

A **trait** defines shared behavior, method signatures a type implements with `impl Trait for Type` ([16.1](https://learnrust.net/chapter-16/introduction-to-traits/)); the **orphan rule** requires you own the trait or the type. Trait methods can have **default bodies** that implementers inherit and may override, and defaults can call required methods, the "small required core plus shared defaults" pattern behind `Iterator` ([16.2](https://learnrust.net/chapter-16/default-method-implementations/)). **Trait bounds** (`T: Trait`) restrict a generic to types with a capability, finally making `largest` compile with `T: PartialOrd` ([16.3](https://learnrust.net/chapter-16/trait-bounds/)); combine with `+`, tidy with `where`. **`impl Trait`** is shorthand for "some type with this trait," in argument position (like a bound) and return position (for unnameable types like closures), with the catch that a return-position `impl Trait` is one concrete type ([16.4](https://learnrust.net/chapter-16/impl-trait/)).

**Deriving** asks the compiler to write a trait: `Debug`, `Clone`, `Copy`, `PartialEq`/`Eq`, `PartialOrd`/`Ord`, `Hash`, `Default`, each requires all fields to implement it, and a trait is derivable when "do it per field and combine" is obviously right ([16.5](https://learnrust.net/chapter-16/deriving-traits/)). **`Display`** is the non-derivable, human-facing `{}`, implemented by hand and unlocking `to_string()` free ([16.6](https://learnrust.net/chapter-16/display-and-tostring/)). **Operators are traits** in `std::ops` (`Add`, `Mul`, `Index`, `Neg`...), so overloading is just implementing a trait, one lesson where C++ needs sixteen ([16.7](https://learnrust.net/chapter-16/operator-overloading/)). **`From`/`Into`** are the blessed conversions (implement `From`, get `Into` free), **`TryFrom`** the fallible version, and **`?` uses `From`** to convert error types as it propagates ([16.8](https://learnrust.net/chapter-16/from-and-into/)).

**Trait objects** (`dyn Trait`, usually `Box<dyn Trait>` or `&dyn Trait`) give runtime polymorphism and let a collection hold mixed types sharing a trait, via dynamic dispatch through a vtable, the runtime counterpart to generics' compile-time static dispatch ([16.9](https://learnrust.net/chapter-16/trait-objects/)). And Rust has **no inheritance**: composition reuses data, default trait methods reuse behavior, trait objects give polymorphism, the three jobs inheritance bundled, now separate ([16.10](https://learnrust.net/chapter-16/code-reuse-without-inheritance/)).

## Quiz time

**Question #1**

What's the difference between static dispatch (generics with bounds) and dynamic dispatch (trait objects), and when do you need the latter?

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

Static dispatch resolves the method at compile time and monomorphizes a copy per type (fast, larger binary, types fixed at compile time). Dynamic dispatch (`dyn Trait`) looks up the method at runtime via a vtable (one shared copy, tiny per-call cost, can hold mixed types). You need trait objects when a collection must hold *different* types sharing a trait, or when a type is chosen at runtime; otherwise prefer generics.

</details>

**Question #2**

For each, say whether you'd derive the trait or implement it by hand, and why:

a) `Debug` for a config struct.
b) `Display` for a `Money` type that should print `$12.50`.
c) `PartialEq` for a `Color` where two colors are equal if RGB matches, ignoring an attached `name` field.

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

a) Derive: `Debug` has an obvious per-field form. b) Implement by hand: `Display` is human-facing and has no automatic form; you choose `$12.50`. c) Implement by hand: the derived `PartialEq` would compare *all* fields including `name`, but you want equality to ignore `name`, so the mechanical version is wrong for this type.

</details>

**Question #3**

Why does implementing `From<Celsius> for Fahrenheit` also let you write `celsius_value.into()`?

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

Because the standard library has a blanket implementation that provides `Into<B> for A` whenever `From<A> for B` exists. So implementing `From<Celsius> for Fahrenheit` automatically gives `Into<Fahrenheit> for Celsius`, and `celsius_value.into()` (with the target type known from context) does the same conversion as `Fahrenheit::from(celsius_value)`.

</details>

**Question #4**

The capstone. Define a `Shape` trait with a required `area(&self) -> f64` and a *default* `describe(&self) -> String` that uses `area`. Implement it for a `Circle { radius: f64 }` and a `Rectangle { width: f64, height: f64 }`. Then build a `Vec<Box<dyn Shape>>` holding one of each, and print each shape's description. Derive `Debug` on the structs.

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

```rust
trait Shape {
    fn area(&self) -> f64;

    fn describe(&self) -> String {
        format!("a shape with area {:.2}", self.area())
    }
}

#[derive(Debug)]
struct Circle {
    radius: f64,
}

#[derive(Debug)]
struct Rectangle {
    width: f64,
    height: f64,
}

impl Shape for Circle {
    fn area(&self) -> f64 {
        3.14159 * self.radius * self.radius
    }
}

impl Shape for Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }

    fn describe(&self) -> String {
        format!("a {}x{} rectangle, area {:.2}", self.width, self.height, self.area())
    }
}

fn main() {
    let shapes: Vec<Box<dyn Shape>> = vec![
        Box::new(Circle { radius: 2.0 }),
        Box::new(Rectangle { width: 3.0, height: 4.0 }),
    ];

    for shape in &shapes {
        println!("{}", shape.describe());
    }
}
```

```
a shape with area 12.57
a 3x4 rectangle, area 12.00
```

Design notes. `Shape` has one required method (`area`) and a default `describe` built on it (lesson [16.2](https://learnrust.net/chapter-16/default-method-implementations/)). `Circle` implements only `area` and inherits the default `describe`; `Rectangle` overrides `describe` with a more specific message. The `Vec<Box<dyn Shape>>` holds both types together via trait objects (lesson [16.9](https://learnrust.net/chapter-16/trait-objects/)), and the loop dispatches `describe` dynamically to each. The `{:.2}` is precision formatting from lesson [5.5](https://learnrust.net/chapter-5/formatting-strings/). This one small program uses required and default trait methods, overriding, and trait objects, the heart of the chapter.

</details>

You've reached the structural summit. There's one piece of the reference machinery still implicit: every reference has a **lifetime**, and chapter 17 finally writes it down, paying lesson [10.8](https://learnrust.net/chapter-10/ownership-in-structs/)'s IOU and explaining the `<'a>` you've been carefully avoiding since chapter 9.

## Sitemap

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