Difference between static ref and structure-owned ref

I have this deref specialization code:

trait Foo { fn bar(&mut self) {} }

impl Foo for () {}
impl<T> Foo for &mut T where T: Foo {}

struct S<'v, T> { value: &'v mut T }

impl<'v, T> Foo for S<'v, T> {
    fn bar(&mut self) {
        struct DerefWrap<T>(T);
        struct Wrap<T>(T);
        
        impl<T> std::ops::Deref for DerefWrap<T> {
            type Target = T;
        
            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }
        
        impl<T> std::ops::DerefMut for DerefWrap<T> {
            fn deref_mut(&mut self) -> &mut Self::Target {
                &mut self.0
            }
        }
        
        impl<T: Foo> Foo for DerefWrap<Wrap<&mut T>> {
            fn bar(&mut self) {
                println!("ok")
            }
        }
        impl<T> Foo for Wrap<T> {
            fn bar(&mut self) {
                println!("nope")
            }
        }
        
        let mut value = &mut ();
        DerefWrap(Wrap(&mut value)).bar(); // Works
        DerefWrap(Wrap(&mut self.value)).bar(); // Doesn't work. Why?
    }
}

fn main() {
    let mut x = S { value: &mut () };
    x.bar();
}

Why does the first call to bar work (Print ok), while the second one doesn't? (It prints nope.) The parameters are the same: &mut &mut () vs &mut &mut (). Is there some obscure difference between member data and let bindings I haven't considered?

Thank you.

The T type parameter on S needs a Foo bound, then it works.

Further explanation: this is a particular case of the general principle that generic code cannot make use of any properties of T that aren't specified in the bounds on T.

Usually, you would get a compilation error, but when Deref is involved, methods can end up shadowing each other. (A “fun” edge case where this shows up in ordinary Rust code is where you've neglected to specify T: Clone, and then calling some_t.clone() (where some_t is an &T) will choose the Clone impl for &T, thus producing an unhelpful copy of the reference.)

Yes, but the point of using deref specialization is exactly to accept T while changing the behavior depending on whether T implements Foo or not. More info: [Generalized Autoref-Based Specialization · Lukasʼ Blog]

Second paragraph of that article:

One thing might be worth clarifying up front: the adopted version described here does not solve the main limitation of autoref-based specialization, namely specializing in a generic context. For example, given fn foo<T: Clone>() , you cannot specialize for T: Copy in that function with autoref-based specialization.

That's what you're trying to do.

1 Like

Aww... I heard about the technique and thought it'd be most appropiate for my case so I used it without looking at the limits. Thank you very much!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.