Blanket impl of AsRef for Deref
There are currently FIXMEs here and here in the core AsRef and AsMut impls to change the implementations for & and &mut to a blanket impl over Deref and DerefMut. However, this blanket impl results in a number of conflicts (even with deref). Because of this, I opened https://github.com/rust-lang/rust/pull/45378 to remove the FIXMEs, but @withoutboats pointed out that we could potentially use intersection specialization to resolve the issue.
I'm not sure exactly what this impl (or set of impls) would look like, so I've closed the PR and opened this issue to track the resolution of the FIXMEs.
Hi, & may I have a question here about the blanket impl,
impl<T: ?Sized, U: ?Sized> AsRef<U> for &T where T: AsRef<U> { fn as_ref(&self) -> &U { <T as AsRef<U>>::as_ref(*self) } }It is implementing
impl AsRef<U> for &T, then what is the type ofselfinfn as_ref(&self) -> &U? My understanding isself: &&T, is this correct?Richard Meng at 2019-07-29 05:54:08
There's currently a FIXME in the libcore
AsRefandAsMutimpls to change the implementations for&and&mutto a blanket impl overAsRef.@cramertj: I assume that text is supposed to read: "to change the implementations for
&and&mutto a blanket impl over ~~AsRef~~Deref." Working links to the corresponding FIXMEs can be found here and here.Jan Behrens at 2022-07-20 06:23:25
It is implementing
impl AsRef<U> for &T, then what is the type ofselfinfn as_ref(&self) -> &U? My understanding isself: &&T, is this correct?Yes, see Playground.
Jan Behrens at 2022-07-20 06:24:57
However, this blanket impl results in a number of conflicts (even with
deref).Disregarding other existing implementations, there would still be some problems with the mutable case (
DerefMut), because types which implementDerefMutmight not want to only provide anAsMutimplementation in regard to theirDeref::Targetbut also, for example,AsMut<Vec<T>> for Vec<T>. See this post on IRLO and the following example:// This blanket implementation conflicts with the following 6 implementations /* impl<T, U> AsMut<U> for T where T: ?Sized + DerefMut, U: ?Sized, <T as Deref>::Target: AsMut<U>, { fn as_mut(&mut self) -> &mut U { self.deref_mut().as_mut() } } */ // Would be covered by blanket implementation impl<'a, T, U> AsMut<U> for &'a mut T where T: ?Sized + AsMut<U>, U: ?Sized, { fn as_mut(&mut self) -> &mut U { self.deref_mut().as_mut() } } // Would be covered by blanket implementation impl<T, U> AsMut<U> for Box<T> where T: ?Sized + AsMut<U>, U: ?Sized, { fn as_mut(&mut self) -> &mut U { self.deref_mut().as_mut() } } // Would NOT covered by blanket implementation // because `<String as Deref>::Target` is `str` and not `String` impl AsMut<String> for String { fn 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 AsMut<str> for String { fn 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> AsMut<Vec<T>> for Vec<T> { fn 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> AsMut<[T]> for Vec<T> { fn as_mut(&mut self) -> &mut [T] { self } }This problem doesn't seem to be exist for the
AsRefcase.AFAIK, even if all existing implementations could be rewritten, the conflict above cannot be solved through negative bounds or specialization yet (though this might change in future). However, it would still possible to provide individual/manual implementations like the above for generic smart pointers such as
Box.That said, PR #28811 introduced different implementations for smart pointers, which was arguably a mistake as pointed out in this post on IRLO by me.
This mistake is difficult to fix. Generally code should refrain from using
.as_ref()or.as_mut()for the sole purpose of dereferencing a smart pointer, but some code may rely on things like the following (example from the docs) to work:fn add_one<T: AsMut<u64>>(num: &mut T) { *num.as_mut() += 1; } let mut boxed_num = Box::new(0); add_one(&mut boxed_num); assert_eq!(*boxed_num, 1);(Note that in PR #99460 I suggest to replace that example, due to the reasons given here, such that new code will not expose this problem.)
Moreover, problems with type inference (i.e. missing type annotations) may arise when adding new implementations to
AsReforAsMutdue to the way these traits are currently unfortunately used. (Playground to demonstrate this, and post on IRLO on that matter)A potential solution has been outlined by @CAD97 here:
std could perhaps incrementally move towards this by introducing a lint against calling .as_ref()/.borrow() on concrete types (challenge: only when the result is not uniquely constrained by a separate constraint than available impls). Then, new impls causing inference breakage only can be provided with pre-warned breakage (alternatively, use edition-dependent method lookup to avoid the breakage).
This may or may not be feasible, but doesn't seem to be easy in either way. I would like if this issue could be fixed because it would provide a consistent API (see Playground). In regard to inconsistencies, also refer to #98905 and the first post in the referenced IRLO thread.
Jan Behrens at 2022-07-20 07:10:02
@JanBeh
I assume that text is supposed to read: "to change the implementations for
&and&mutto a blanket impl over ~AsRef~Deref." Working links to the corresponding FIXMEs can be found here and here.I've updated the top issue text -- hope that helps!
Josh Stone at 2022-09-15 17:04:27