Semantics of AsRef

If I understand it right, then a blanket implementation like in this other FIXME (note that the DerefMut bound is missing there) would (in stable Rust) be incompatible with an implementation of AsMut<P> for P for some type P that is also DerefMut (which includes AsMut<Vec<T>> for Vec<T>).

Thus, while the first FIXME (for AsRef and Deref) could (theoretically, i.e. disregarding backward-compatibility) be implemented in stable Rust to create a consistent AsRef implementation, doing that for AsMut and DerefMut would not work unless useful implementations like AsMut<Vec<T>> for Vec<T> would be removed.


Of course, it would still be possible to manually add such "auto-dereferencing" implementations like in the FIXME for generic smart pointers such as Rc, Arc, etc. while providing a trivial implementation that just returns &mut self for non-generic [1] smart pointers such as String, Vec<T>, etc.


I created a Playground to illustrate that:

/* … */

// This blanket implementation works fine
impl<T, U> MyAsRef<U> for T
where
    T: ?Sized + MyDeref,
    U: ?Sized,
    <T as MyDeref>::Target: MyAsRef<U>,
{
    fn my_as_ref(&self) -> &U {
        self.my_deref().my_as_ref()
    }
}

// This blanket implementation conflicts with the following 6 implementations
/*
impl<T, U> MyAsMut<U> for T
where
    T: ?Sized + MyDerefMut,
    U: ?Sized,
    <T as MyDeref>::Target: MyAsMut<U>,
{
    fn my_as_mut(&mut self) -> &mut U {
        self.my_deref_mut().my_as_mut()
    }
}
*/

// Would be covered by blanket implementation
impl<'a, T, U> MyAsMut<U> for &'a mut T
where
    T: ?Sized + MyAsMut<U>,
    U: ?Sized,
{
    fn my_as_mut(&mut self) -> &mut U {
        self.my_deref_mut().my_as_mut()
    }
}

// Would be covered by blanket implementation
impl<T, U> MyAsMut<U> for Box<T>
where
    T: ?Sized + MyAsMut<U>,
    U: ?Sized,
{
    fn my_as_mut(&mut self) -> &mut U {
        self.my_deref_mut().my_as_mut()
    }
}

// Would NOT covered by blanket implementation
// because `<String as Deref>::Target` is `str` and not `String`
impl MyAsMut<String> for String {
    fn my_as_mut(&mut self) -> &mut String {
        self
    }
}

// Would be covered by blanket implementation
// because `<String as Deref>::Target` is `str`
// and `str` implements `AsMut<str>`
impl MyAsMut<str> for String {
    fn my_as_mut(&mut self) -> &mut str {
        self
    }
}

// Would NOT covered by blanket implementation
// because `<Vec<T> as Deref>::Target` is `[T]` and not `Vec<T>`
impl<T> MyAsMut<Vec<T>> for Vec<T> {
    fn my_as_mut(&mut self) -> &mut Vec<T> {
        self
    }
}

// Would be covered by blanket implementation
// because `<Vec<T> as Deref>::Target` is `[T]`
// and `[T]` implements `AsMut<[T]>`
impl<T> MyAsMut<[T]> for Vec<T> {
    fn my_as_mut(&mut self) -> &mut [T] {
        self
    }
}

/* … */

(Playground)

This is just to experiment with these traits a bit to see what's possible to do without features like coherence-impacting negative bounds or specialization with the goal to get a better understanding for AsRef and AsMut, and to be able to deduce whether these features could help in future. Due to the conflicting implementations (that are all desirable), I assume that coherence-impacting negative bounds are not sufficient here, but that specialization is needed to provide such an "auto-dereferencing" blanket implementation for AsMut (edit: under the premise that AsMut<String> for String, AsMut<Vec<T>> for Vec<T>, etc. shall be implemented).

I do not want to imply that any of this could currently (or even with specialization) be implemented, as it would break existent code anyway (if I understand it right, especially where .as_ref() is used to dereference generic smart pointers as demonstrated in my previous post).


I tested whether the "auto-dereferencing" blanket implementation for AsMut in combination with impl AsMut<String> for String and impl<T> AsMut<Vec<T>> for Vec<T> compiles with specialization, but it doesn't (Playground).


  1. Vec<T> in this context isn't entirely generic because even if being generic over T it always points to a slice, i.e. Deref::Target = [T] and not Deref::Target = T. ↩︎