Today I stumbled across the following oddity of Rust’s operator/method lookup:
use std::ops::Add;
use std::fmt::Debug;
#[derive(Debug)]
struct Foo(i32);
impl Add for &Foo{
type Output = Foo;
fn add(self, other: &Foo) -> Foo {
Foo(self.0 + other.0)
}
}
fn main() {
// Works as expected.
println!("Sum: {:?}", Foo(1).add(&Foo(1)));
// Error. The &Foo::add candidate is not considered.
println!("Sum: {:?}", Foo(1) + &Foo(1));
}
This was unexpected for me, as I assumed that operators were simply syntactic sugar
for their respective trait method call.
This quirk apparently also leads to many seemingly redundant operator implementations in the standard library and in custom user types.
Especially for custom containers this further worsens the combinatorial explosion
of traits to implement for no (apparent ?) benefit.
Can anybody explain why this is the case?
4
In rust values are not auto referenced as mentioned by @kmdreko. You are getting an error because Add
is implemented for &Foo
and not Foo
. So if you try to do
use std::ops::Add;
use std::fmt::Debug;
#[derive(Debug)]
struct Foo(i32);
impl Add for &Foo {
type Output = Foo;
fn add(self, other: &Self) -> Self::Output {
Self(self.0 + other.0)
}
}
fn main() {
println!("Sum: {:?}", &Foo(1) + &Foo(1)); // Notice the `&` before the first `Foo`
}
It works as expected. playground
Or you could add another Add
implementation to Foo
. Here you are specifying the Rhs
type as &Foo
instead of the default Self
use std::ops::Add;
use std::fmt::Debug;
#[derive(Debug)]
struct Foo(i32);
impl Add for &Foo{
type Output = Foo;
fn add(self, other: &Foo) -> Self::Output {
Foo(self.0 + other.0)
}
}
impl Add<&Foo> for Foo {
type Output = Foo;
fn add(self, other: &Foo) -> Self::Output {
Foo(self.0 + other.0)
}
}
fn main() {
println!("Sum: {:?}", &Foo(1) + &Foo(1));
println!("Sum: {:?}", Foo(1) + &Foo(1));
}
playground
3