Operator overloading
Now that we have a basic understanding of what traits are, let's circle back to operator overloading.
Operator overloading is the ability to define custom behavior for operators like +
, -
, *
, /
, ==
, !=
, etc.
Operators are traits
In Rust, operators are traits.
For each operator, there is a corresponding trait that defines the behavior of that operator.
By implementing that trait for your type, you unlock the usage of the corresponding operators.
For example, the PartialEq
trait defines the behavior of
the ==
and !=
operators:
#![allow(unused)] fn main() { // The `PartialEq` trait definition, from Rust's standard library // (It is *slightly* simplified, for now) pub trait PartialEq { // Required method // // `Self` is a Rust keyword that stands for // "the type that is implementing the trait" fn eq(&self, other: &Self) -> bool; // Provided method fn ne(&self, other: &Self) -> bool { ... } } }
When you write x == y
the compiler will look for an implementation of the PartialEq
trait for the types of x
and y
and replace x == y
with x.eq(y)
. It's syntactic sugar!
This is the correspondence for the main operators:
Arithmetic operators live in the std::ops
module,
while comparison ones live in the std::cmp
module.
Default implementations
The comment on PartialEq::ne
states that "ne
is a provided method".
It means that PartialEq
provides a default implementation for ne
in the trait definition—the { ... }
elided
block in the definition snippet.
If we expand the elided block, it looks like this:
#![allow(unused)] fn main() { pub trait PartialEq { fn eq(&self, other: &Self) -> bool; fn ne(&self, other: &Self) -> bool { !self.eq(other) } } }
It's what you expect: ne
is the negation of eq
.
Since a default implementation is provided, you can skip implementing ne
when you implement PartialEq
for your type.
It's enough to implement eq
:
#![allow(unused)] fn main() { struct WrappingU8 { inner: u8, } impl PartialEq for WrappingU8 { fn eq(&self, other: &WrappingU8) -> bool { self.inner == other.inner } // No `ne` implementation here } }
You are not forced to use the default implementation though. You can choose to override it when you implement the trait:
#![allow(unused)] fn main() { struct MyType; impl PartialEq for MyType { fn eq(&self, other: &MyType) -> bool { // Custom implementation } fn ne(&self, other: &MyType) -> bool { // Custom implementation } } }
Exercise
The exercise for this section is located in 04_traits/03_operator_overloading