Description
arbitrary_self_types
(v1 RFC, v2 amending RFC) is currently being considered for stabilization in #135881. This feature, designed to support methods which take self
by an expanded set of user-defined types like self: SmartPointer<T>
, self: CppRef<T>
, makes several changes to method resolution.
How method lookup works today
Today, we search for methods on a type T
in this order (rustc dev guide, reference):
- Methods for type
T
with aSelf
type ofself
,&self
,&mut self
- Deref: while
T
isDeref
,NewT = <T as Deref>::Target
, considerT
,&T
,&mut T
- Unsizing: once
T
is no longerDeref
, ifT == [A; N]
, consider[A]
,&[A]
,&mut [A]
For each considered type, preference is given for inherent methods over extension methods (trait methods).
How method lookup works under arbitrary_self_types
arbitrary_self_types
v2 changes this look up (RFC. reference PR):
- The first and most obvious change is that it uses the
Receiver
trait rather than theDeref
trait for looking up candidate receiver types. This allows for method receivers likefn takes_cppref(self: CppRef<T>)
whereCppRef
is some type that cannot implementDeref<Target = T>
. - However, the more significant change is that
arbitrary_self_types
allows for methods to be defined forimpl MyType { fn foo(self: PtrLike<MyType>) { ... } }
for customPtrlike
types.
Other related outstanding features
- The
arbitrary_self_types_pointers
feature considers*const T
methods for each candidate receiver type which is a*mut T
. - The
pin_ergonomics
feature as documented here consider "method[s] with aPin
that's reborrowed" (note: I, cramertj@, don't actually understand at this time how this changes the candidate or method set).
Why custom autoref
I created this issue because I believe that most uses of arbitrary_self_types
that I'm aware of actually want custom autoref behavior (the exception is external std-likes e.g. RFL's Arc
). That is, rather than extending the candidate set of receiver types, I believe they may instead/also want to modify the per-candidate set of Self
types searched. I want to ensure that the parts of arbitrary_self_types
being stabilized do not hamper our ability to do add autoref behavior (at least for Pin
, if not for custom types).
For example:
struct MyType { ... }
impl MyType {
fn foo(self: CppRef<Self>) { ... }
}
let x = MyType { ... };
x.foo(); // ERROR
Doing lookup for foo
on MyType
, we look for methods taking self: MyType
, self: &MyType
, and self: &mut MyType
, see that there's no receiver impl or unsizing to follow, and give up. What should happen is that CppRef
should behave as &
does (as it is strictly less powerful than &
and can be created from T
"for free").
Note that this is a per-candidate type behavior, as we'd want Box::new(MyType{ ... }).foo()
to work as well. That is, method resolution for foo
on Box<MyType>
should look for foo
as a by-value, by-ref, by-cppref, by-mutref, and by-cppmutref method on candidate types Box<MyType>
and then MyType
.
self: Box<MyType>
,self: &Box<MyType>
,self: CppRef<Box<MyType>>
,self: &mut Box<MyType>
,self: CppMutRef<Box<MyType>>
self: MyType
,self: &MyType
,self: CppRef<MyType>
<<< this one is found and selected
Similarly, we could imagine doing the same thing in order to support by-Pin
methods on types which are Unpin
(Unpin
types can convert from Self
to Pin<&Self>
/Pin<&mut Self>
"for free"):
struct MyType { ... }
impl Future for MyType { fn poll(self: Pin<&mut Self>, ... ) -> ... { ... } }
let x = MyType { ... };
x.poll(..) // ERROR today, we'd like this to work, maybe with something like
impl<T: ?Sized + Unpin> AutoRef<Pin<&T>> for T { ... }
impl<T: ?Sized + Unpin> AutoRefMut<Pin<&mut T>> for T { ... }
Similarly, we'd also love for Pin<&mut Self>
methods to be callable on Pin<Box<Self>>
receiver types, which can create a Pin<&mut Self>
"for free", maybe with an impl like:
impl<T: ?Sized> AutoRef<Pin<&T> for Pin<Box<T>> { ... }
impl<T: ?Sized> AutoRefMut<Pin<&mut T>> for Pin<Box<T>> { ... }
Other examples of autoref-like non-Receiver
s that we'd like to consider in method resolution:
- @veluca93 mentioned having image types that would like to autoref into mutable image views.
- @sarah-quinones brought up the
faer
MatMut
type which is a mutable view over a matrix. Ideally, methods which work on aMatMut
would be callable on a local of typeMat
.
Is this even a thing we can do?
Maybe not. Making method resolution search a (# of autoref types for Self
) * (Receiver/Deref impls for Self
+ unsize) number of types when looking for a method seems expensive, but I don't have a good idea of how expensive.
However, I think this is well-motivated at least for Pin
, and possibly some other select set of types.
Future compatibility issues
At this point this issue is mostly FUD, unfortunately-- I don't have a specific concern besides "method resolution is getting more complicated but maybe in the wrong way." Pin
is already stable as a self
type, so any extensions we make to support at least Pin
-autoref have to be done in a backwards compatible way. Therefore, it may be the case that we're not making anything worse for ourselves by allowing other non-stdlib types to have this ability.
The arbitrary_self_types
v2 RFC does say that arbitrary_self_types
makes it so that "a wider set of locations is searched for methods with those receiver types." I haven't completely understood this-- it seems like the set of places we have to look today for a potential self: Arc<Self>
method is the same set of locations we'd have to look for a self: NonStdArc<Self>
(that is, we have to search both the impls of ...Arc
and Self
as well as any <Self as Receiver>::Target
).
Am I (@cramertj) missing something? Are we committing to other method resolution complexities by stabilizing arbitrary_self_types
that will make it harder to add autoref support for Pin
or CppRef
?