---
title: "21.3 Deref and deref coercion"
description: "The Deref trait that lets a smart pointer stand in for its contents, and the deref coercion that finally explains why &String works as &str."
url: "https://learnrust.net/chapter-21/deref-and-deref-coercion/"
last_updated: "2026-06-13"
---

# 21.3 Deref and deref coercion

Back in lesson [9.7](https://learnrust.net/chapter-9/string-slices/) something quietly magical happened: you passed a `&String` to a function expecting `&str`, and it just worked. We named the mechanism, **deref coercion**, and promised the full explanation once you had smart pointers. You now do. This lesson covers the `Deref` trait, the thing that lets a smart pointer be transparently used as the value it points to, and the coercion that automatically converts between reference types to spare you a pile of explicit dereferences.

## The dereference operator, revisited

You met `*` back in chapter 9 as the way to reach through a reference to the value behind it: if `r` is `&5`, then `*r` is `5`. A `Box<T>` supports the same operator:

```rust
fn main() {
    let x = 5;
    let boxed = Box::new(5);

    println!("{}", x == 5);      // true: comparing an i32
    println!("{}", *boxed == 5); // true: *boxed reaches the i32 inside the box
}
```

```
true
true
```

`*boxed` follows the box to the `i32` on the heap, exactly as `*r` follows a reference. This isn't a coincidence or special-casing for `Box`. The `*` operator on anything that isn't a plain reference is governed by a trait, and any type that implements it gets to participate.

## The Deref trait

The trait is `Deref`. Implementing it for a type says "here's what `*` should produce for me," by returning a reference to some inner value. The standard library implements `Deref` for `Box<T>` to return `&T`, which is why `*boxed` gives you the boxed value. You can implement it yourself; here's a minimal smart pointer that wraps one value, just to see the wiring:

```rust
use std::ops::Deref;

struct MyBox<T>(T);

impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}

impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.0   // return a reference to the wrapped value
    }
}

fn main() {
    let b = MyBox::new(5);
    println!("{}", *b);
}
```

```
5
```

`MyBox` is a tuple struct holding one value. Its `Deref` implementation returns `&self.0`, a reference to that value. With that in place, `*b` works. Under the hood, when you write `*b` Rust actually runs `*(b.deref())`: it calls `deref` to get a `&T`, then applies the *real* dereference to that reference. The `Deref` trait is how a type plugs its own meaning into the `*` operator. (`String` implements `Deref` with `Target = str`, which is the seed of the whole `&String`-as-`&str` story.)

## Deref coercion: the mystery solved

Here's the payoff. **Deref coercion** is a convenience the compiler performs: when you pass a reference to a type that implements `Deref`, and the function wants a reference to the `Deref` *target*, the compiler automatically inserts the `deref` call for you. `&MyBox<String>` can be passed where `&str` is expected, and the compiler chains the coercions to get there.

Watch it work with our `MyBox`:

```rust
fn hello(name: &str) {
    println!("Hello, {name}!");
}

fn main() {
    let m = MyBox::new(String::from("Rust"));
    hello(&m);
}
```

```
Hello, Rust!
```

Trace what the compiler did with `hello(&m)`. We have `&MyBox<String>`, and `hello` wants `&str`. So the compiler applies deref coercion twice: `&MyBox<String>` derefs to `&String` (via `MyBox`'s `Deref`), and `&String` derefs to `&str` (via `String`'s `Deref`). Two automatic `deref` calls, inserted at compile time, turn our wrapper into exactly the `&str` the function needed. Without deref coercion you'd have to write `hello(&(*m)[..])`, manually dereferencing the `MyBox`, then the `String`, then slicing. The coercion makes the ugly explicit form unnecessary.

And *that* is the chapter-9 mystery, fully explained. When you passed a `&String` to a `&str` function back then, deref coercion converted it by calling `String`'s `Deref` (whose target is `str`). It was never a special `String` favor; it's the general `Deref` coercion rule, and it applies to every smart pointer, including the `Box` you just learned and any `MyBox` you write.

> **Key insight**
>
> Deref coercion is why smart pointers feel weightless to use. A `Box<String>`, an `Rc<Vec<i32>>`, a plain `&String`, all of them can be handed to functions written for the inner type, because the compiler silently inserts `deref` calls until the types line up. You write code in terms of the data you care about (`&str`, `&[T]`) and the wrappers get out of the way. The `&String`-works-as-`&str` convenience from chapter 9 was your first sighting of a rule that runs through the entire smart-pointer system.

## Mutable deref, briefly

There's a mutable cousin, `DerefMut`, which does the same for `&mut`: it lets `&mut MyBox<T>` coerce to `&mut T`, so you can reach through a smart pointer to *modify* the inner value. You'll rarely implement it by hand, but it's why `*boxed += 1` works on a `Box<i32>` and why a `&mut String` can be passed where `&mut str` operations are needed. The rule mirrors `Deref` exactly, with mutability threaded through; the coercions still respect chapter 9's borrowing rules (a `&mut` coercion needs a `&mut` source).

## Quiz time

**Question #1**

When you write `*b` for a smart pointer `b`, what does the compiler actually do?

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

It runs `*(b.deref())`: it calls the type's `Deref::deref` method, which returns a reference (`&T`) to the inner value, then applies the real dereference to that reference. The `Deref` trait is how a type defines what `*` means for it. For `Box<T>`, `deref` returns `&T`, so `*b` yields the boxed value.

</details>

**Question #2**

Explain, in terms of `Deref`, why you can pass a `&String` to a function expecting `&str`.

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

`String` implements `Deref` with `Target = str`, so a `&String` can be *deref-coerced* to a `&str`: the compiler automatically inserts a `deref` call to convert it when the function expects `&str`. This is deref coercion, the same general rule that lets any smart pointer (`Box`, a custom `MyBox`) coerce to a reference to its inner type. The chapter-9 convenience was this rule's first appearance.

</details>

**Question #3**

For a `MyBox<String>` and a function `hello(name: &str)`, how many deref coercions does `hello(&m)` perform, and what are they?

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

Two. `&MyBox<String>` coerces to `&String` (via `MyBox`'s `Deref`, target `String`), and `&String` coerces to `&str` (via `String`'s `Deref`, target `str`). The compiler chains them automatically at compile time. Without coercion you'd write the awkward `hello(&(*m)[..])` to do both dereferences and the slice by hand.

</details>

`Deref` controls what happens when you *reach through* a smart pointer. The other defining trait controls what happens when a smart pointer *dies*. The next lesson ([21.4](https://learnrust.net/chapter-21/drop-in-depth/)) covers `Drop`, the cleanup mechanism behind everything that's been freeing itself automatically since chapter 8.

## Sitemap

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