Перегрузка операций
Rust позволяет ограниченную форму перегрузки операций. Есть определенные операции, которые могут быть перегружены. Есть специальные типажи, которые вы можете реализовать для поддержки конкретной операции между типами. В результате чего перегружается операция.
Например, операция + может быть перегружена с помощью типажа Add:
use std::ops::Add;
#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}
impl Add for Point {
    type Output = Point;
    fn add(self, other: Point) -> Point {
        Point { x: self.x + other.x, y: self.y + other.y }
    }
}
fn main() {
    let p1 = Point { x: 1, y: 0 };
    let p2 = Point { x: 2, y: 3 };
    let p3 = p1 + p2;
    println!("{:?}", p3);
}
В main мы можем использовать операцию + для двух Point, так как мы
реализовали типаж Add<Output=Point> для Point.
Есть целый ряд операций, которые могут быть перегружены таким образом, и все
связанные с этим типажи расположены в модуле [std::ops]stdops. Проверьте эту
часть документации для получения полного списка.
Реализация этих типажей следует паттерну. Давайте посмотрим на типаж
[Add]add более детально:
# mod foo {
pub trait Add<RHS = Self> {
    type Output;
    fn add(self, rhs: RHS) -> Self::Output;
}
# }
В общей сложности здесь присутствуют три типа: тип impl Add, который мы
реализуем, тип RHS, который по умолчанию равен Self и тип Output. Для
выражения let z = x + y: x — это тип Self, y — это тип RHS, а z -
это тип Self::Output.
# struct Point;
# use std::ops::Add;
impl Add<i32> for Point {
    type Output = f64;
    fn add(self, rhs: i32) -> f64 {
        // add an i32 to a Point and get an f64
# 1.0
    }
}
позволит вам сделать следующее:
let p: Point = // ...
let x: f64 = p + 2i32;
Использование типажей операций в обобщённых структурах
Теперь, когда мы знаем, как реализованы типажи операций, мы можем реализовать
наш типаж HasArea и структуру Square из главы о типажах более
общим образом:
use std::ops::Mul;
trait HasArea<T> {
    fn area(&self) -> T;
}
struct Square<T> {
    x: T,
    y: T,
    side: T,
}
impl<T> HasArea<T> for Square<T>
        where T: Mul<Output=T> + Copy {
    fn area(&self) -> T {
        self.side * self.side
    }
}
fn main() {
    let s = Square {
        x: 0.0f64,
        y: 0.0f64,
        side: 12.0f64,
    };
    println!("Площадь s: {}", s.area());
}
Мы просто объявляем тип-параметр T и используем его вместо f64 в определении
HasArea и Square. В реализации нужно сделать более хитрые изменения:
impl<T> HasArea<T> for Square<T>
        where T: Mul<Output=T> + Copy { ... }
Чтобы реализовать area, мы должны мочь умножить операнды друг на друга,
поэтому мы объявляем T как реализующий std::ops::Mul. Как и Add, Mul
принимает параметр Output: т.к. мы знаем, что числа не меняют своего типа,
когда их умножают, Output также объявлен как T. T также должен
поддерживать копирование, чтобы Rust не пытался переместить self.side в
возвращаемое значение.