Chapter 5 of the book talks about Automatic Dereferencing with the notable example of method calls.
Call Expressions have a search-process to find the method in an instance.method()
call. This algorithm enables ergonomic read and use since (*instance).method()
, (&instance).method()
and such are later added by the compiler.
For example in instance.method()
the compiler will know the type of instance
but the guessing-feature (par above) doesn't know whether it's for instance
, *instance
, &instance
, &mut instance
...
So it creates candidates until the it matches, say fn method(&mut self)
.
A complete example of that process is given in the reference:
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]
.
- My first question is: Are the candidate types independent of the type of the instance ? In other words, do
instance
,&instace
,&&instance
all generate the same candidate list? I assume so, or that one is a subset of the others, but I'm unsure. - PS: What happens if 2 of the types in the list have the same method? Like
x
and&x
? (or it could be that the sentence below is correct, I am hesitating now.)
From that candidate list only one matches the receiver's type (that in the method signature.)
Case 1: No traits
Expectedly so, this fails to compile:
Snippet 1: No Traits
struct A { }
impl A {
fn foo(self) {}
fn foo(&mut self) {}
fn foo (&self) {}
}
However, this overloading of foo
is actually allowed for trait
s (and compounded by A
's foo
as well):
Case 2: Default Traits
Snippet 2: Default Implementations
struct A;
impl A {
// not so relevant but left for completeness
fn foo(self) {}
}
impl B for A {} // implements the defaults for brevity
impl C for A {} // ditto
trait B {
fn foo(&self) {}
}
// note that this does NOT clash with B
// due to the search process
trait C {
fn foo(&mut self) {}
// fn foo(&self) {} // this would clash with B
}
I think this is error prone. If you Derive
a trait, or simply implement an external trait with a default {}
that could lead to bugs since a.foo()
could be choosing methods from any implemented traits.
Some of the rules I found
- Compiler only complains when they resolve to the same method and the same receiver; a name+method like
foo(&self)
inB
andC
clashes, but won't if one is not&self
.- When they clash, one needs to disambiguate the code with a full path
B::foo(&c)
orC::..
- When they clash, one needs to disambiguate the code with a full path
- Compiler takes first
self
then&self
then&mut self
; this is important for homonymous methods.
There isn't much of a question for this last part, but it is just very confusing, so any notes, remarks or corrections are welcome.