2

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

Yang
  • 69
  • 7
  • I think `rustc_diagnostic_item` is an annotation for the Clippy analysis. But I cannot find any clippy rule that depends on it. Maybe they were all replaced with more specific hints (this deref is not needed, this cast is not needed...), and now the no-op annotation is itself a no-op ;-). – rodrigo Dec 22 '22 at 12:30
  • I think closing this question is in error. @Yang, the point here is that some trait impls for built-in types are special. This is true for dereferencing built-in types like `&T` or `Box`, which the compiler knows how to do internally. For reasons of cohesion, the standard library defines these impls so that trait resolution works as expected, yet their bodies are actually never executed. The situation is similar with `1+1`, where there is an impl for [std::ops::Add](https://doc.rust-lang.org/stable/src/core/ops/arith.rs.html#94) for all numeric types that are defined in terms of ... `1+1`. – user2722968 Dec 22 '22 at 12:51
  • I have edited the question, but it is still closed – Yang Dec 23 '22 at 19:20
  • I added my own understanding to the question, just to confirm if I really got it. Please take another look. – Yang Jan 04 '23 at 21:52

1 Answers1

2

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;

kmdreko
  • 42,554
  • 6
  • 57
  • 106
  • Thank you so much for taking the time to answer my question, I'm not sure I understand the meaning of Deref, I want to ask: Deref for &T will return &T itself, is it meaningless? Is it just a marker? – Yang Jan 04 '23 at 21:11
  • @Yang There are many "identity" implementations: `From for T`, `Borrow for T`, etc. No, it is not meaningless; if it did not exist then the example code above that one would expect to work would not. "Marker" has a specific meaning for traits and which means they do not have any associated functions. – kmdreko Jan 04 '23 at 21:19
  • thanks for the reply,I see in source code: #[rustc_diagnostic_item = "noop_method_deref"]. Does that mean this method won't be called? Since deref() it returns itself (accepts &T -> than return &T), calling it is also useless? – Yang Jan 04 '23 at 21:33
  • I added my own understanding to the question, just to confirm if I really got it. Please take another look. – Yang Jan 04 '23 at 21:52
  • @Yang I'm not sure, but given that it is a "diagnostic" attribute, it is more likely related to how error messages are generated. – kmdreko Jan 04 '23 at 21:56
  • @Yang I have no idea what your edit is supposed to mean. Are you asking a new question? Is there a specific bit that you're unsure of? Please be mindful of making your questions focused; your original question is about `&T` implementing `Deref`. And the linked answers you've dismissed should already alleviate some of your other concerns. – kmdreko Jan 04 '23 at 21:59
  • I added code and comments in the new modification, those comments are to verify that I really understand what the blanket implementation does, I think I got it, so thank you really. – Yang Jan 05 '23 at 09:26