@anon65535354 I think you will find most of us reading/replying here are humans and just let the compiler do its thing. It removes having a "hard" problem.
Separately, I've written multiple experiments to try to show the seeming success of the first procedure:
struct OwnType;
impl OwnType {
fn takes_ref(&self){
println!("You are shared or unique.");
}
fn takes_unique(&mut self){
println!("You are unique.");
}
}
fn main(){
// case: unique reference.
let mut a = OwnType;
let mutable_ref = &mut a;
mutable_ref.takes_ref(); // with first method
// Finds OwnType::takes_ref(&self); it coerces the stored-apart &mut OwnType to &OwnType
mutable_ref.takes_unique(); // with second method
// case: shared reference
let b = OwnType;
let shared_ref = &b;
shared_ref.takes_ref();
// self=OwnType, and puts the &OwnType we had in-there.
// b_r.takes_unique() // fails
// Finds method in OwnType, but can't turn &OwnType into &mut OwnType
// what for calls to a and b ?
a.takes_ref(); // again, it inserts & to the dereferenced type!
b.takes_ref(); // ditto
a.takes_unique(); // again, it inserts & to the dereferenced type!
// b.takes_unique(); // can't insert &mut to b because it's not mut.
}
This step is incorrect. The fact that the impl block has a Self-type of OwnType is completely irrelevant to method search. All that matters is the receiver type that results after substituting the definition of Self, i.e. &OwnType in the case of a_method. Imagine that the compiler rewrites the code
impl OwnType {
fn a_method(&self) {...}
}
into a collection of every method it knows about, independent of impl (this is not real Rust syntax):
mod all_the_methods_in_the_world {
// all of your methods
fn a_method(self: &OwnType) {...}
// all of the methods from std too
fn push<T>(self: Vec<T>, value: T) {...}
}
Method search just looks at all_the_methods_in_the_world regardless of where they came from (except that traits are only searched if in scope). In this case, method search finds a method with receiver type &OwnType (as the first candidate!) and calls it.
I don't think I understand how is it irrelevant. I'm aware you understand this well; but is the snippet below and accompanying explanation sound to you?
let a = OwnType(); //implements a_method(&self)
let ar = &a;
ar.a_method() // takes &self
Goes through the method search procedure, where it gets &OwnType, &&OwnType, &mut &OwnType, OwnType, &OwnType (again), &mut OwnType and it then finds that OwnType is the winning candidate for .a_method()
Having obtained OwnType::a_method(..) it still has to see what to do with ar to match the signature of .a_method(&self).
In this case, it's nothing because ar matches exactly the &self.
But had we called a.a_method() instead, it would need to reference a i.e turn it into &a.
Is this part something we agree on, or where is my mistake?
What to do is already determined by where in the method search procedure it came from. If the search tried adding &, then the call to the found method must add &. If the search tried dereferencing, the call must dereference. There is no separate step to choose a coercion; the choice is made by the search process.
Consider this code:
trait HasRefMethod {
fn by_ref(&self);
}
trait HasMoveMethod: Sized {
fn by_move(self);
}
struct Foo {}
impl HasRefMethod for Foo {
fn by_ref(&self) {}
// This could also be written as:
// fn by_ref(self: &Foo) {}
}
impl<'a> HasMoveMethod for &'a Foo {
fn by_move(self) {}
// This could also be written as:
// fn by_move(self: &'a Foo) {}
}
fn main() {
let x = Foo {};
x.by_ref(); // autoref
x.by_move(); // also autoref
}
In the two method calls in this code, the receiver types are identical;[1] they are both &Foo. Method search will insert an implicit autoref &x to make the provided value type (Foo) match the methods’ receiver type (&Foo). It does not matter that one impl block is for Foo and one is for &Foo. Only the receiver type matters, and both receiver types are &Foo.
I think I disagree with that. I don't think they are in sync but they are independent.
Take the snippet below:
let a = OwnType(); //implements a_method(&self)
a.a_method()
finds the method right in the calling type "T"; yet the method needs &self as per the signature, so it must add & to the variable a and so passes &a.
How does the process you described explain this case?
As for your first function, I agree with that one. And here is how I would use the procedure for those calls:
It finds that by_ref is on T, so it searches no further. But it needs an &self, so x will be &-ded so to speak or referenced. I think this is what you called "autoref" which I have not found much info about.
Here this is new for me, I have not read about implementing methods on &Ts but my interpretation would be that it must be adding a reference before starting the search (Which is not what the reference states unless I assume that the initial variable could be T or any reference they implicitly add. But I thought this wouldn't exist because then it would become infinite: why not to & it many more times if you implement it on &&&&&'a Foo?)
Just in case I'm using different terminology by receiver I mean the bit before the dot . in the call-expr.
I don't mean the self parameter name in the method's definition. I think those can be different, and they have to in cases such as my example above. I think what you are saying is that they are the same (maybe) but I challenge you to explain the snippet I gave at the top of this reply (or what is the same, your example with by_ref).
It does not find the method under the type OwnType. It finds the method with the receiver type&OwnType. The Self type OwnType is irrelevant except by contributing to defining the receiver type &Self = &OwnType.
The method search procedure tries T first, but then tries &T second. That's the part of the algorithm “Then, for each candidate T , add &T and &mut T to the list immediately after T.” This procedure always terminates because it doesn’t try adding &more than once (per candidate).
“Receiver” can mean either of these things, but when I’ve been talking about “receiver type” I mean the type in the method signature, the one that is in the end actually passed to the method, not the type of the expression in the receiver position of the method call expression.
It is matching the type of the receiver expression (the first thing it defines) in the caller, to the receiver type of the candidate methods. The sentence I quoted in the previous reply is referring to the latter.
search for a visible method with a receiver of that type
(bolding is mine)
By "with" here it means a method "that has" a receiver of that type. It does not mean "matches to a receiver" which might be how you interpreted it.