Problem with &self in method call

Why is the follow code not compiling?

trait Trait {
    fn foo(&mut self, x: i32);
}

struct Foo;

impl Foo {
    fn foo(&self) {
        println!("Foo::foo");
    }
}

impl Trait for Foo {
    fn foo(&mut self, x: i32) {
        self.foo(); // (&*self).foo();
        println!("Trait::foo {}", x);
    }
}

The error message:

error[E0061]: this function takes 1 parameter but 0 parameters were supplied
  --> src/lib.rs:15:14
   |
2  |     fn foo(&mut self, x: i32);
   |     -------------------------- defined here
...
15 |         self.foo(); // (&*self).foo();
   |              ^^^ expected 1 parameter

But if I change call self.foo() to (&*self).foo() or Self::foo(self), then program compiles successfuly.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6defd297a48edb38e3c12b95d75fb15f

Method resolution uses very simple rules. The compiler looks at the method "receiver" (the thing before the dot, in this case self, which has type &mut Foo) and checks it if has a method called foo. If it doesn't, then it tries either borrowing or dereferencing the receiver and checks again. It continues this process until it finds a matching method or reaches its recursion limit. As soon as it finds a method with a matching name, it stops.

This system is dumb but predictable. It has the disadvantage that sometimes it doesn't find the right method when there is a name conflict, and you need to be more explicit, as in your example. But it also has advantages over a "smarter" system that continues searching and looking for "better" matches after finding a possibly-incorrect one. Some of these advantages are on the implementation side; smarter searching would be slower and more complex, and could create circular dependencies between compiler passes. Deeper searching could also make type inference fail more often (requiring additional type annotations) or cause method resolution to change in surprising ways when code changes.

3 Likes