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:

// 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:

OperatorTrait
+Add
-Sub
*Mul
/Div
%Rem
== and !=PartialEq
<, >, <=, and >=PartialOrd

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:

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:

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:

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