---
title: How does Deref affect borrowing
summary: The Deref trait enables automatic coercion of smart pointer references to inner data references, simplifying function calls and borrowing.
lang: en
slug: how-does-deref-affect-borrowing
published: 2026-04-17T12:05:42+00:00
updated: 2026-04-25T22:00:13+00:00
canonical: https://www.rustfaq.org/en/how-does-deref-affect-borrowing/
---

# How does Deref affect borrowing



## When a wrapper needs to disappear

You're writing a function that greets a user. It takes `&str` because that's the universal text reference in Rust. You have a `Box<String>` holding the name. You try to pass `&my_box`. The compiler rejects you with E0308 (mismatched types). You try `&*my_box`. It works, but now your code is littered with asterisks. You want the wrapper to just behave like the data inside, at least when you're reading it.

That's what `Deref` is for. It tells the compiler that a reference to your wrapper can automatically turn into a reference to the inner value. The wrapper becomes invisible to the caller.

## The automatic look-inside rule

`Deref` is a trait in `std::ops`. When a type implements `Deref`, it promises it can hand out a reference to something else. The "something else" is called the target.

Think of a library card system. The card itself isn't the book. But the library has a rule: if you show the card, the librarian automatically fetches the book for you. You don't need to know where the book is stored. You just show the card, and you get the book.

In Rust, the card is your smart pointer or wrapper. The book is the inner data. `Deref` is the rule that lets the compiler fetch the book automatically whenever a function asks for it.

This only works for references. `Deref` turns `&Wrapper` into `&Inner`. It does not turn `Wrapper` into `Inner`. You can't move the value out of a wrapper using `Deref`. You can only borrow through it.

## Minimal example

Here's a custom box that implements `Deref`. The code shows the trait definition and how coercion removes the need for manual dereferencing.

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

/// A wrapper that holds any type T.
struct MyBox<T>(T);

/// Implement Deref so MyBox can act like T when borrowed.
impl<T> Deref for MyBox<T> {
    /// The target type is the inner value.
    type Target = T;

    /// Return a reference to the inner value.
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

/// A function that expects a string slice.
fn print_name(name: &str) {
    println!("Name: {}", name);
}

fn main() {
    // Create a box containing a String.
    let box_str = MyBox(String::from("Rust"));

    // The compiler coerces &MyBox<String> to &str automatically.
    // No asterisk needed.
    print_name(&box_str);
}
```

The call `print_name(&box_str)` works because the compiler inserts the deref calls for you. It sees you have `&MyBox<String>` and the function wants `&str`. It checks if `MyBox` implements `Deref`. It does. The target is `String`. The compiler pretends you wrote `print_name(box_str.deref())`. Now you have `&String`. The function still wants `&str`. The compiler checks if `String` implements `Deref`. It does. The target is `str`. The compiler inserts another call. You get `&str`. The types match.

## What the compiler actually does

Deref coercion is not magic. It is code generation. The compiler inserts calls to the `.deref()` method at compile time. Those calls run at runtime.

This matters if your `deref` implementation has side effects. The side effects will happen every time coercion occurs.

The compiler follows a strict path. It looks at the type you have. It checks for `Deref`. If found, it moves to the target type. It repeats this process. Rust limits the chain to three steps. You can't nest wrappers infinitely and expect coercion to resolve it. The compiler stops after three derefs and gives up.

Coercion only happens when the compiler needs a reference. It triggers in function arguments, method calls, and assignments where the target type is a reference. It does not trigger for moves. If you try to pass a `Box<T>` to a function expecting `T`, `Deref` won't help. You need to move the value out explicitly, usually with `*box` or `Box::into_inner`.

## Real code: The cost of transparency

In real code, `Deref` is often used to build smart pointers. `Box`, `Rc`, `Arc`, and `Vec` all implement `Deref`. This is why you can pass `&vec` to a function expecting `&[T]`. The vector coerces to a slice.

Sometimes you build your own wrapper to add metadata. Here's a string that tracks how many times it's been accessed.

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

/// A string that counts how often it's dereferenced.
struct TrackedString {
    inner: String,
    hits: Cell<u32>,
}

impl TrackedString {
    fn new(s: &str) -> Self {
        TrackedString {
            inner: s.to_string(),
            hits: Cell::new(0),
        }
    }

    fn hit_count(&self) -> u32 {
        self.hits.get()
    }
}

/// Implement Deref to expose the String.
impl Deref for TrackedString {
    type Target = String;

    fn deref(&self) -> &Self::Target {
        // Increment the counter every time we deref.
        self.hits.set(self.hits.get() + 1);
        &self.inner
    }
}

/// A function that only cares about the text.
fn process(text: &str) {
    println!("Got: {}", text);
}

fn main() {
    let tracked = TrackedString::new("hello");

    // Coercion inserts .deref() calls.
    // &TrackedString -> &String -> &str.
    // Two derefs happen. The counter increments twice.
    process(&tracked);

    println!("Hits: {}", tracked.hit_count());
}
```

Run this and you'll see the hit count is 2. The compiler inserted two `.deref()` calls to bridge the gap from `TrackedString` to `str`. Each call ran the body of `deref`, incrementing the counter.

This reveals a hard rule about `Deref`. The implementation must be cheap and side-effect free. If `deref` mutates state, logs, or allocates, you break the mental model of every Rust programmer. Coercion happens silently. Callers don't expect a function call to trigger hidden logic just because they passed a reference.

Keep `deref` pure. Side effects here break the mental model of every Rust programmer.

## Pitfalls and compiler errors

Deref coercion simplifies code, but it introduces traps.

**Mutable access requires `DerefMut`.** Implementing `Deref` only grants immutable access. If you have `&mut Wrapper` and try to mutate the inner value, the compiler rejects you. It can only coerce to `&Inner`, not `&mut Inner`. You'll get E0596 (cannot borrow as mutable) or a "method not found" error because the mutable method requires `&mut self`. To support mutation, you must also implement `DerefMut`.

**Coercion doesn't help with moves.** If a function takes ownership of `T`, passing `&Wrapper` won't work. The compiler won't deref to get a value. It only derefs to get references. You'll see E0308 (mismatched types). You need to move the value out explicitly.

**Chaining has a limit.** The compiler only chains three derefs. If you wrap a value in four layers of `Deref` types, coercion stops. You'll get a type mismatch error. Flatten your wrappers or deref manually.

**Deref can hide type changes.** Because coercion happens automatically, the type of an expression can change silently. `&box_str` might be `&String` in one context and `&str` in another. This can make debugging harder. If you're confused about types, check the coercion chain. The compiler is inserting code you didn't write.

Don't hide state behind deref. Keep the implementation trivial.

## Deref versus AsRef

A common question is when to use `Deref` versus `AsRef`. Both let you convert a type to a reference. They serve different purposes.

`Deref` is for transparency. It says "this type is a wrapper around another type." It enables coercion. It's structural. `Box<T>` implements `Deref<Target=T>` because a box is literally a pointer to T.

`AsRef` is for conversion. It says "this type can be viewed as T." It's a trait bound, not a coercion mechanism. `AsRef` doesn't trigger automatically. You call `.as_ref()` explicitly. It's useful for generic functions that accept multiple input types.

For example, a function that processes text might accept `String`, `&String`, `&str`, or `Vec<u8>`. You can't use `Deref` for all of these. `Vec<u8>` doesn't deref to `str`. But you can use `AsRef<[u8]>` or `AsRef<str>` to write a generic function that handles many types.

Use `Deref` when you're building a smart pointer or a transparent wrapper. Use `AsRef` when you're writing a function that needs to accept multiple types and convert them to a common reference.

Deref is for transparency. AsRef is for conversion. Don't mix them up.

## When to use Deref

Use `Deref` when you want a wrapper type to behave transparently like the inner type for read-only access. This lets you pass `&Wrapper` to functions expecting `&Inner` without the caller writing `.deref()` or `*`.

Use `DerefMut` when the wrapper needs to support mutation through the reference. Implementing `Deref` alone only grants immutable access; adding `DerefMut` allows `&mut Wrapper` to coerce to `&mut Inner`.

Use explicit accessor methods when the wrapper has state or invariants that must be checked before access. `Deref` is too coarse-grained if you need to validate data on every read. Expose specific methods instead.

Reach for `AsRef` when you need a one-off conversion between types without implying a structural wrapper relationship. `AsRef` is for "I can be viewed as T", while `Deref` is for "I am a container of T".

Stick to standard smart pointers like `Box` and `Rc` for most use cases. Implementing `Deref` yourself is rare outside of library code building new abstractions.

Trust the borrow checker. If `Deref` makes the types confusing, you're probably overusing it.

## Where to go next

- [What is the difference between &T and T in function signatures](/en/what-is-the-difference-between-t-and-t-in-function-signatures/)
- [Error E0507: "cannot move out of borrowed content" — How to Fix](/en/error-e0507-cannot-move-out-of-borrowed-content-how-to-fix/)
- [How to Use RefCell<T> for Interior Mutability in Rust](/en/how-to-use-refcellt-for-interior-mutability-in-rust/)