---
title: "16.10 Code reuse without inheritance"
description: "Rust has no inheritance; how composition, default trait methods, and trait objects cover what inheritance was used for, and what to reach for when you miss it."
url: "https://learnrust.net/chapter-16/code-reuse-without-inheritance/"
last_updated: "2026-06-13"
---

# 16.10 Code reuse without inheritance

If you're coming from C++, Java, or Python, a question has been building for the whole chapter: where's inheritance? The answer is that Rust doesn't have it, on purpose, and this lesson is the honest accounting of what you use instead. It's a lesson C++ veterans specifically search for, because the instinct to reach for a base class is strong and Rust redirects it three different ways depending on what you were actually trying to do.

## Inheritance did two jobs

The reason "what replaces inheritance?" doesn't have a one-word answer is that inheritance bundled two separate things together, and Rust unbundles them. Classical inheritance gave you (1) **code reuse**: a subclass gets the parent's fields and methods without rewriting them, and (2) **polymorphism**: a function taking a `Shape` works on any subclass of `Shape`, calling the right overridden method. These are different needs that happened to share a feature, and Rust provides each one separately, which turns out to be cleaner.

## Reuse through composition

For the reuse half, Rust's answer is **composition**: instead of a type *being* another type (inheritance), it *contains* one (a field). Where you'd reach for `class Manager extends Employee`, Rust uses a struct with an `Employee` field:

```rust
struct Employee {
    name: String,
    salary: u64,
}

impl Employee {
    fn pay_stub(&self) -> String {
        format!("{}: ${}", self.name, self.salary)
    }
}

struct Manager {
    employee: Employee,    // composition: a Manager *has an* Employee
    reports: u32,
}

impl Manager {
    fn summary(&self) -> String {
        format!("{} manages {} people", self.employee.pay_stub(), self.reports)
    }
}
```

`Manager` reuses `Employee`'s data and behavior by holding one and delegating to it (`self.employee.pay_stub()`). This is the "has-a" relationship, and the wider software-design world has long argued it's usually the better default anyway, summed up in the maxim "prefer composition over inheritance." Rust simply makes composition the only option, sidestepping the well-known problems of deep inheritance hierarchies (fragile base classes, surprising overrides, the diamond problem). The small cost is that delegation is explicit: you write `self.employee.pay_stub()` rather than inheriting `pay_stub` silently. That explicitness is generally considered a feature, not a tax.

## Shared behavior through default methods

For the "many types share an implementation" half of reuse, the tool is **default trait methods** (lesson [16.2](https://learnrust.net/chapter-16/default-method-implementations/)). Where a base class provides a method all subclasses inherit, a trait provides a default method all implementers inherit:

```rust
trait Greet {
    fn name(&self) -> String;

    fn hello(&self) -> String {           // shared default, "inherited" by all
        format!("Hello, I'm {}", self.name())
    }
}
```

Every type implementing `Greet` gets `hello` for free and only supplies `name`. That's the inheritance-of-behavior pattern, achieved with a trait instead of a base class, and crucially a type can implement *many* traits (gaining many sets of shared behavior), where most languages let a class inherit from only one parent. Composition for shared *data*, default trait methods for shared *behavior*.

## Polymorphism through trait objects

For the polymorphism half, "a function that works on any subtype, calling the right method", the tool is **trait objects** (lesson [16.9](https://learnrust.net/chapter-16/trait-objects/)). A function taking `&dyn Draw` works on any type implementing `Draw`, dispatching to the right `draw` at runtime, which is exactly what a function taking a `Shape*` and calling a virtual method did in C++. So the polymorphism you'd get from a base-class pointer, you get from a trait object, without the class hierarchy.

> **Key insight**
>
> Inheritance bundled reuse and polymorphism; Rust unbundles them. Reuse of *data* is composition (a field). Reuse of *behavior* is default trait methods. Polymorphism is trait objects (`dyn Trait`). Each was a job inheritance did, and each gets a focused tool. The result has no base classes, no override surprises, and no single-inheritance limit, and a type can mix in as many traits as it needs. What you lose is the convenience of writing one `extends` and getting everything at once; what you gain is that each kind of sharing is explicit and independent.

## When you'll miss it, and what to reach for

Honesty, since C++ veterans will test this. There *are* moments the lack of inheritance stings: a deep hierarchy of types that genuinely share a lot of fields *and* behavior *and* need runtime polymorphism, all at once, is more verbose in Rust because you assemble it from three tools instead of one keyword. When you feel that, the move is to ask which job you actually need: shared fields (compose, with a field), shared behavior (a trait with defaults), runtime dispatch over a mix (a `dyn Trait`), and often you discover you needed only one or two of the three, not the whole bundle. The cases where you truly need all three deeply nested are rarer than inheritance-shaped habits suggest, and when they arise, a trait with default methods plus composition plus `Box<dyn Trait>` covers them, just spelled out rather than implied.

> **Best practice**
>
> When you catch yourself wanting inheritance, name the specific thing you want. "I want to reuse these fields", compose. "I want these types to share this method", default trait method. "I want to treat different types uniformly at runtime", trait object. Reaching for "a base class" is usually reaching for two or three of these at once; separating them almost always yields a cleaner design, and frequently reveals you only needed one.

## Quiz time

**Question #1**

What two distinct jobs did classical inheritance bundle together, and what does Rust use for each?

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

Code reuse (a subclass gets the parent's fields/methods) and polymorphism (one function handles any subtype, calling the right method). Rust splits them: composition (a field) for reusing data, default trait methods for reusing behavior, and trait objects (`dyn Trait`) for runtime polymorphism. Each job gets a focused tool instead of one bundled feature.

</details>

**Question #2**

Rust has no `class Manager extends Employee`. How do you give `Manager` access to `Employee`'s data and behavior?

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

Composition: give `Manager` a field of type `Employee` (`employee: Employee`) and delegate to it (`self.employee.pay_stub()`). `Manager` *has an* `Employee` rather than *being* one. The delegation is explicit, which is the "prefer composition over inheritance" approach that's widely considered the better default anyway.

</details>

**Question #3**

A coworker wants a base class so several types can share one `validate` method and be stored together in a list and processed uniformly. Which Rust tools address each part?

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

Sharing the `validate` method: a trait with a default `validate` method that all the types implement (lesson [16.2](https://learnrust.net/chapter-16/default-method-implementations/)). Storing them together and processing uniformly: a `Vec<Box<dyn TheTrait>>` of trait objects with dynamic dispatch (lesson [16.9](https://learnrust.net/chapter-16/trait-objects/)). No base class needed; the trait covers both the shared behavior and the polymorphism.

</details>

That completes the traits chapter, the structural center of Rust and the clearest proof of the "shorter than C++" thesis. The summary is next. Then chapter 17 finally writes out the thing you've used implicitly since chapter 9 and deferred from lesson [10.8](https://learnrust.net/chapter-10/ownership-in-structs/): **lifetimes**, the annotations that let references live in structs and function signatures.

## Sitemap

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