Associated Types and Advanced Traits

Operator Overloading

Operator overloading in Rust customizes the behavior of built-in operators (+, -, *, [], ==, <, !, etc.) for user-defined types by implementing the corresponding traits in std::ops and std::cmp.

Rules of Operator Overloading in Rust

  1. Trait-backed only: Every overloadable operator desugars to a method call on a trait in std::ops or std::cmp.
  2. Fixed operator set: You cannot define brand-new operator symbols (such as ** or <=>).
  3. Fixed precedence and associativity: Implementing Mul and Add always obeys standard * before + precedence.
  4. Excluded operators: Short-circuiting logical operators (&&, ||), assignment (=), range syntax (.., ..=), and the error propagation operator (?, via unstable Try) cannot be overloaded in stable user code.

Overloadable Operators and Their Traits

Category Operators Module & Trait Names
Binary arithmetic +, -, *, /, % std::ops::{Add, Sub, Mul, Div, Rem}
Unary arithmetic / bitwise -x, !x std::ops::{Neg, Not}
Bitwise & shift &, |, ^, <<, >> std::ops::{BitAnd, BitOr, BitXor, Shl, Shr}
Compound assignment +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= std::ops::{AddAssign, SubAssign, MulAssign, DivAssign, RemAssign, BitAndAssign, BitOrAssign, BitXorAssign, ShlAssign, ShrAssign}
Indexing & dereferencing a[i], *x std::ops::{Index, IndexMut, Deref, DerefMut}
Equality & comparison ==, !=, <, <=, >, >= std::cmp::{PartialEq, PartialOrd}

Compiler Desugaring of Operators

When the compiler encounters an overloaded operator, it rewrites the expression into the underlying trait method call:

a + b       ──>   std::ops::Add::add(a, b)
a += b      ──>   std::ops::AddAssign::add_assign(&mut a, b)
-a          ──>   std::ops::Neg::neg(a)
a[i]        ──>   *std::ops::Index::index(&a, i)
a[i] = v    ──>   *std::ops::IndexMut::index_mut(&mut a, i) = v
a == b      ──>   std::cmp::PartialEq::eq(&a, &b)
use std::ops::{Add, Neg};

#[derive(Debug, Clone, Copy, PartialEq)]
struct Complex {
    re: f64,
    im: f64,
}

impl Add for Complex {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        Self {
            re: self.re + rhs.re,
            im: self.im + rhs.im,
        }
    }
}

impl Neg for Complex {
    type Output = Self;

    fn neg(self) -> Self::Output {
        Self {
            re: -self.re,
            im: -self.im,
        }
    }
}

fn main() {
    let z1 = Complex { re: 1.5, im: -2.0 };
    let z2 = Complex { re: 0.5, im: 4.0 };

    let sum = z1 + z2;
    let inverted = -sum;

    println!("sum:      {sum:?}");
    println!("inverted: {inverted:?}");
}
sum:      Complex { re: 2.0, im: 2.0 }
inverted: Complex { re: -2.0, im: -2.0 }