I\'m learning/experimenting with Rust, and in all the elegance that I find in this language, there is one peculiarity that baffles me and seems totally out of place.
The Rust reference has a chapter about the method call expression. I copied the most important part below. Reminder: we are talking about an expression recv.m()
, where recv
is called "receiver expression" below.
The first step is to build a list of candidate receiver types. Obtain these by repeatedly dereferencing the receiver expression's type, adding each type encountered to the list, then finally attempting an unsized coercion at the end, and adding the result type if that is successful. Then, for each candidate
T
, add&T
and&mut T
to the list immediately afterT
.For instance, if the receiver has type
Box<[i32;2]>
, then the candidate types will beBox<[i32;2]>
,&Box<[i32;2]>
,&mut Box<[i32;2]>
,[i32; 2]
(by dereferencing),&[i32; 2]
,&mut [i32; 2]
,[i32]
(by unsized coercion),&[i32]
, and finally&mut [i32]
.Then, for each candidate type
T
, search for a visible method with a receiver of that type in the following places:
T
's inherent methods (methods implemented directly onT
[¹]).- Any of the methods provided by a visible trait implemented by
T
. [...]
(Note about [¹]: I actually think this phrasing is wrong. I've opened an issue. Let's just ignore that sentence in the parenthesis.)
Let's go through a few examples from your code in detail! For your examples, we can ignore the part about "unsized coercion" and "inherent methods".
(*X{val:42}).m()
: the receiver expression's type is i32
. We perform these steps:
i32
cannot be dereferenced, so we are already done with step 1. List: [i32]
&i32
and &mut i32
. List: [i32, &i32, &mut i32]
::m
which has the receiver type i32
. So we are already done.So far so easy. Now let's pick a more difficult example: (&&A).m()
. The receiver expression's type is &&A
. We perform these steps:
&&A
can be dereferenced to &A
, so we add that to the list. &A
can be dereferenced again, so we also add A
to the list. A
cannot be dereferenced, so we stop. List: [&&A, &A, A]
T
in the list, we add &T
and &mut T
immediately after T
. List: [&&A, &&&A, &mut &&A, &A, &&A, &mut &A, A, &A, &mut A]
&&A
, so we go to the next type in the list.<&&&A as M>::m
which indeed has the receiver type &&&A
. So we are done.Here are the candidate receiver lists for all of your examples. The type that is enclosed in ⟪x⟫
is the one that "won", i.e. the first type for which a fitting method could be found. Also remember that the first type in the list is always the receiver expression's type. Lastly, I formatted the list in lines of three, but that's just formatting: this list is a flat list.
(*X{val:42}).m()
→ ::m
[⟪i32⟫, &i32, &mut i32]
X{val:42}.m()
→ ::m
[⟪X⟫, &X, &mut X,
i32, &i32, &mut i32]
(&X{val:42}).m()
→ <&X as M>::m
[⟪&X⟫, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
(&&X{val:42}).m()
→ <&&X as M>::m
[⟪&&X⟫, &&&X, &mut &&X,
&X, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
(&&&X{val:42}).m()
→ <&&&X as M>::m
[⟪&&&X⟫, &&&&X, &mut &&&X,
&&X, &&&X, &mut &&X,
&X, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
(&&&&X{val:42}).m()
→ <&&&X as M>::m
[&&&&X, &&&&&X, &mut &&&&X,
⟪&&&X⟫, &&&&X, &mut &&&X,
&&X, &&&X, &mut &&X,
&X, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
(&&&&&X{val:42}).m()
→ <&&&X as M>::m
[&&&&&X, &&&&&&X, &mut &&&&&X,
&&&&X, &&&&&X, &mut &&&&X,
⟪&&&X⟫, &&&&X, &mut &&&X,
&&X, &&&X, &mut &&X,
&X, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
(*X{val:42}).refm()
→ ::refm
[i32, ⟪&i32⟫, &mut i32]
X{val:42}.refm()
→ ::refm
[X, ⟪&X⟫, &mut X,
i32, &i32, &mut i32]
(&X{val:42}).refm()
→ ::refm
[⟪&X⟫, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
(&&X{val:42}).refm()
→ <&X as RefM>::refm
[⟪&&X⟫, &&&X, &mut &&X,
&X, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
(&&&X{val:42}).refm()
→ <&&X as RefM>::refm
[⟪&&&X⟫, &&&&X, &mut &&&X,
&&X, &&&X, &mut &&X,
&X, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
(&&&&X{val:42}).refm()
→ <&&&X as RefM>::refm
[⟪&&&&X⟫, &&&&&X, &mut &&&&X,
&&&X, &&&&X, &mut &&&X,
&&X, &&&X, &mut &&X,
&X, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
(&&&&&X{val:42}).refm()
→ <&&&X as RefM>::refm
[&&&&&X, &&&&&&X, &mut &&&&&X,
⟪&&&&X⟫, &&&&&X, &mut &&&&X,
&&&X, &&&&X, &mut &&&X,
&&X, &&&X, &mut &&X,
&X, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
Y{val:42}.refm()
→ ::refm
[Y, &Y, &mut Y,
i32, ⟪&i32⟫, &mut i32]
Z{val:Y{val:42}}.refm()
→ ::refm
[Z, &Z, &mut Z,
Y, &Y, &mut Y,
i32, ⟪&i32⟫, &mut i32]
A.m()
→ ::m
[⟪A⟫, &A, &mut A]
(&A).m()
→ ::m
[&A, &&A, &mut &A,
⟪A⟫, &A, &mut A]
(&&A).m()
→ <&&&A as M>::m
[&&A, ⟪&&&A⟫, &mut &&A,
&A, &&A, &mut &A,
A, &A, &mut A]
(&&&A).m()
→ <&&&A as M>::m
[⟪&&&A⟫, &&&&A, &mut &&&A,
&&A, &&&A, &mut &&A,
&A, &&A, &mut &A,
A, &A, &mut A]
A.refm()
→ ::refm
[A, ⟪&A⟫, &mut A]
(&A).refm()
→ ::refm
[⟪&A⟫, &&A, &mut &A,
A, &A, &mut A]
(&&A).refm()
→ ::refm
[&&A, &&&A, &mut &&A,
⟪&A⟫, &&A, &mut &A,
A, &A, &mut A]
(&&&A).refm()
→ <&&&A as RefM>::refm
[&&&A, ⟪&&&&A⟫, &mut &&&A,
&&A, &&&A, &mut &&A,
&A, &&A, &mut &A,
A, &A, &mut A]