Inconsistent method call resolution

play

trait Abs {
  fn abs(self) -> Self;
}

impl Abs for i64 {
  fn abs(self) -> Self {
    -self
  }
}

fn main() {
  let x = 42;
  println!("{}", x.abs()); // -42
  println!("{}", x.abs()); // 42
}
  1. Please explain to me what is going on here.
  2. Will this situation improve in the future or not?
  3. Any links to discussions of this issue?

Thanks!

3 Likes

Oh this is a fun one!
The following is maybe a bit hand-wavy with respect to some details.

let x = 42 <- at this point the compiler treats this as {integer}, that is it could be any of the integer types.
On the first x.abs() it now needs a concrete type, so it starts looking. It favors local implementations and finds your trait Abs that's implemented for i64. So it picks that and makes x: i64.
On the second x.abs() it nows x: i64 and thus favors the direct implementation that Rust brings along: i64::abs. So it calls that, not your trait implementation. (you can btw see that if you e.g. add a println in your trait implementation, it only gets called once).

I didn't look if there's any issues filed about this that would have more discussion. But maybe with this explanation you'd be able to find some.

8 Likes

Thanks! I understand it now. This behavior surprises me!
Is it exactly defined in the rust reference? Or is it a breaking change and needs an edition gate if we make 2 syntactically identical method calls to resolve to the same method?

6 Likes