Does Debug formatter use dereference?

Viewed 58

I am confused about how does the Debug formatter get to the end of the reference chain.

My understanding is that println!("{:?}", x) expands to something like:

... print(&x) ...

which borrows x instead of moving/copying it.

Now this prints MyNumber(1) instead of expected &MyNumber(1):

#[derive(Debug)]
struct MyNumber(u8);

fn main() {
    let x = MyNumber(1);
    println!("{:?}", x);
}

from which I assume that the Debug formatter follows the generic Deref implementation defined for all references by the Rust language itself (impl<T> Deref for &T).

If so, then why the following code prints MyNumber(1) instead of just 1?

use std::ops::{Deref};

#[derive(Debug)]
struct MyNumber(u8);

impl Deref for MyNumber {
    type Target = u8;

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

fn main() {
    let x = MyNumber(1);
    println!("{:?}", x);
}

It seems to me that the Debug formatter follows the Deref implemented on &T by the Rust language itself but ignores the Deref implemented on my custom type.

Does anyone know why this happens? Any help much appreciated!

1 Answers

Does anyone know why this happens? Any help much appreciated!

If you check the documentation for Debug then search around you will find:

impl<'_, T> Debug for &'_ T where
    T: Debug + ?Sized, 

Meaning there is a bulk-implementation specifically on references which delegates to the underlying object. Which in your case is MyNumber, at which point that's what's used.

There is no implementation for Deref, instead there are implementations for each smart pointer type (if you search in the page you'll find implementations for Box and Rc).

So your code prints MyNumber(1) because that's what it's told to do: it invokes the Debug implementation on MyNumber. If you want something different, you need to implement (not derive) Debug to delegate to its underlying type.

Related