An unclear semantic point about invoking method for implementation of a trait

trait MyTrait{
	fn show(self);
}
impl MyTrait for i32{
    fn show(self) {
        //todo!()
		println!("i32");
    }
}
fn main(){
   let i = 0;
   let rf = &i;
   rf.show();  // print i32
}

Invoking the method show on the reference(whose type is &i32) is ok. However, Don't I implement MyTrait merely for type i32, why it works on the type &i32? IMO, i32 and &i32 are different types. Then, we continually add an implementation, such that the code will be

trait MyTrait{
	fn show(self);
}
impl MyTrait for i32{
    fn show(self) {
        //todo!()
		println!("i32");
    }
}

impl MyTrait for &i32{
    fn show(self) {
        //todo!()
		println!("&i32");
    }
}
fn main() {
   let i = 0;
   let rf = &i;
   rf.show();  // print &i32
   i.show();  // print i32
}

Compare with the first example, the invocation of rf.show now prints &i32, which exactly matches the type of the implementation(i.e. &i32).

So, what are the rules here when we invoke a method of a trait on an entity? How does it match? Does it first match the type of entity with the type on which the trait is implemented? Or something else?

https://doc.rust-lang.org/reference/expressions/method-call-expr.html

1 Like

The rules are described here. An exact type match on the receiver will take precedence, but there can also be an autoref or (multiple levels of) autoderef, et cetera.

1 Like

Beside what vague linked, do note that your first example works is only because i32 is Copy.

1 Like

rf.show(); is desugared to <&i32 as MyTrait>::show(rf) because the receiver type &i32 has this method.
Similarly i.show(); is desugared to <i32 as MyTrait>::show(i).

The rule is for a type T, the candidate types are

  • T
  • &T
  • &mut T
  • *T
  • &*T
  • &mut *T
  • coercion to U
  • &U
  • &mut U

The order is important.
And inherent methods are prior to trait methods.

2 Likes

They take precedence in case of a tie, but that doesn't override the receiver order.

2 Likes

I realize the importance of the call methods rules when dealing with Quiz #31 and #23, thanks to dtolnay :laughing:

1 Like

That quiz is evil.

That is to say, the type of receiver(i.e. the type of self) is the principle to determine whether the expression could match the method or not, Right? In other words, we first build a set of types that are sourced from transforming the type of the expression on which the method is called, then we just match each element of the set to the type of receiver of the found methods.

Basicly yes.

The book calls it automatic referencing and dereferencing, but doesn't elaborate on it.
The reference describes it in details.

The first step for me is: desugar self / &self / &mut self and desugar Self

impl &Type {
  fn f(self) {} // self: Self means self: &Type, so this method is for `&Type` directly
}
impl Type {
  fn f(&self) {} // self: &Self means self: &Type, so this method is for `&Type` directly, too
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.