Deref is a misleading name

⚓ Rust    📅 2026-02-19    👤 surdeus    👁️ 1      

surdeus

Info

This post is auto-generated from RSS feed The Rust Programming Language Forum - Latest topics. Source: Deref is a misleading name

Having worked with Rust for a couple of years, I found myself occasionally implementing Deref on some custom types, e.g. if they represent newtype wrappers around other types. I have never questioned the terms like Deref for the trait, its method deref() or Deref coercion. However, after having dug into the implementation and actual actions that the trait's method performs, I believe that deref is a horribly misleading and unfit name for the trait and so is deref coercion.

The trait does not dereference

The first and foremost thing that struck me is, that the trait Deref, respectively its method deref() does not perform dereferencing at all. You can see this clearly by its signature.

We define a type to "dereference" to via type Target;. The method fn deref(&self) -> &Self::Target; then converts a reference to the implementor (&self) to a reference of the target type. That's right Deref::deref() returns a reference, not a dereferenced type.

While the documentation clearly states how Deref works and what its intentions are, this goes against my intuition of a trait being named after the action it performs. Deref does not dereference. It changes how dereferencing a type behaves. Other traits in the standard library are named after what they do. E.g. AsRef returns a reference to target type, Add adds stuff. A better name for it may have been RefMap because it actually maps one reference to another type of reference, similar to AsRef<T>, but with the whole deref coercion magic. Which brings me to the next issue.

Deref coercion is not about dereferencing

If deref coercion was about dereferencing, I'd expect this to work:

use std::ops::Deref;

#[derive(Clone, Copy, Debug)]
struct Foo;

impl Foo {
    fn foo(&self) -> &'static str {
        "I am foo."
    }
}

#[derive(Clone, Copy, Debug)]
struct Bar {
    foo: Foo,
}

impl Deref for Bar {
    type Target = Foo;

    fn deref(&self) -> &Self::Target {
        &self.foo
    }
}

fn print_foo(foo: Foo) {
    println!("{}", foo.foo());
}

fn main() {
    let bar = Bar { foo: Foo };
    print_foo(*&bar);
}

In the line of the call print_foo(*&bar) I'd naively expect the following to happen:

  1. I create a temporary reference to bar, which is of type Bar, that will have type &Bar.
  2. I immediately dereference this reference. Since Bar implements Deref with Target = Foo it should™ dereference to a type Foo.

But no, it doesn't. Because Deref does not actually dereference anything. It just maps reference types. So I'd need to add an extra dereference operator to make the magic happen: print_foo(**&bar); which is equivalent to print_foo(*bar);.
Wait what? bar is not a reference. It is of type Bar, not &Bar. How can I dereference a non-reference type? Well, because due to Deref the code actually desugars to print_foo(*Deref::deref(&bar));. I.e. The Deref trait's method 'deref() gets passed a reference to bar, which it maps to another type of reference, namely &Foo, which then in turn gets dereferenced by the dereference operator *.

On the other hand, the following code works fine:

use std::ops::Deref;

#[derive(Clone, Copy, Debug)]
struct Foo;

impl Foo {
    fn foo(&self) -> &'static str {
        "I am foo."
    }
}

#[derive(Clone, Copy, Debug)]
struct Bar {
    foo: Foo,
}

impl Deref for Bar {
    type Target = Foo;

    fn deref(&self) -> &Self::Target {
        &self.foo
    }
}

fn print_foo(foo: &Foo) {
    println!("{}", foo.foo());
}

fn main() {
    let bar = Bar { foo: Foo };
    print_foo(&bar);
    print_foo(Deref::deref(&bar));
}

There is no dereferencing involved here. We simply take a reference of one type which is automagically ("deref coercion") or explicitly mapped to another reference. Nothing is being dereferenced here.

Where am I going with this?

Probably nowhere. The trait is in standard library for longer than I have been using Rust, so it's not going to go away or be renamed. I rest my case that its name is misleading, as is the term deref coercion. Maybe the official docs can more clearly point out that the trait, despite its name is not about dereferencing and that neither is deref coercion, which rather is reference coercion. Feel free to correct me if I'm wrong with my assertions.

1 post - 1 participant

Read full topic

🏷️ Rust_feed