Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do we need the blanket implementation of Deref trait for &T?

Tags:

rust

Implementing Deref for smart pointers makes accessing the data behind them convenient, which is why they implement Deref. On the other hand, the rules regarding Deref and DerefMut were designed specifically to accommodate smart pointers. Because of this, Deref should only be implemented for smart pointers to avoid confusion.

I understand its usefulness,but I don't know what's the use of this blanket implementation of the Deref trait.

#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_deref", issue = "88955")]
impl<T: ?Sized> const Deref for &T {
    type Target = T;

    #[rustc_diagnostic_item = "noop_method_deref"]
    fn deref(&self) -> &T {
        *self
    }
}

As the book says here https://doc.rust-lang.org/book/ch15-02-deref.html#treating-a-type-like-a-reference-by-implementing-the-deref-trait

Without the Deref trait, the compiler can only dereference & references. The deref method gives the compiler the ability to take a value of any type that implements Deref and call the deref method to get a & reference that it knows how to dereference.

Why do we need this blanket implementation of Deref trait for &T type if the compiler knows how to deference it?

What are Rust's exact auto-dereferencing rules? this question does not answer my question, because my question is what is the use of this blanket implementation.

Why is the return type of Deref::deref itself a reference? this question didn't solve my doubts either. in the first answer the author did not explain why there is the blanket implementation of Deref trait for &T type.

I added the code from the author of the first answer, with my own modifications

Rust playground https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=25387e87ea7a7ef349df9e9b6671f6e7

I added some comments to make sure I really got it

use std::ops::Deref;
use std::rc::Rc;

fn foo<T: Deref<Target = i32>>(t: T) {
    println!("{}", *t);
    print_type_of(&t);
    print_type_of(&(*t));
    println!("-----------------");
}

fn main() {
    // Ok, because &i32 implements Deref<Target = i32>, so deref() return &i32 and the compiler can deref it again to get i32
    // Thanks to the blanket implementation
    foo(&100i32);
    // No, because &&i32.deref() return &&i32, so the compiler can't deref it twice to get i32
    foo(&&100i32);
    // Ok, because Box<i32> implements Deref<Target = i32>, so deref() return &i32 and the compiler can deref it again to get i32
    // But this has nothing to do with the blanket implementation of Deref for &T
    // Because Box is not a reference type
    foo(Box::new(42i32));
    // Ok, because Rc<i32> implements Deref<Target = i32>, so deref() return &i32 and the compiler can deref it again to get i32
    foo(Rc::new(5i32));
}

fn print_type_of<T>(_: &T) {
    println!("{}", std::any::type_name::<T>())
}

I'm not sure I understand your answer

like image 360
Lorenzo Yang Avatar asked Oct 21 '25 07:10

Lorenzo Yang


1 Answers

A Deref implementation exists for &T the same reason any trait exists: genericism.

Even if the compiler knows how to dereference references innately because they are built-in to the language, the Deref implementation should still exist so &T can be used in a context that requires Deref. Consider this function:

fn foo<T: Deref<Target = i32>>(t: T) {
    println!("{}", *t);
}

See it working on the playground;

It can accept references, Boxs, Rcs, etc. If the Deref implementation did not exist for references, then you could use the dereferencing operator * on it directly, but you could not pass it to this function that dereferences what is passed. That would be inconsistent and confusing for no reason.


I'm not sure what your example is supposed to prove; it can't be compiled outside the standard library and your assessment of the types is incorrect (Self is &T so self is of type &&T, which when dereferenced returns a &T, which matches the return type). Implementing an equivalent trait works just fine:

trait Deref {
    type Target: ?Sized;
    fn deref(&self) -> &Self::Target;
}

impl<T: ?Sized> Deref for &T {
    type Target = T;
    fn deref(&self) -> &T {
        *self
    }
}

See it working on the playground;

like image 109
kmdreko Avatar answered Oct 23 '25 21:10

kmdreko