Step 4 of 7
Operator overloading
Rust's operators are traits in std::ops. a + b is really Add::add(a, b), so implementing Add for your type makes + work on it. This is called operator overloading. It's perfect for math-like types: vectors, money, matrices, durations.
use std::ops::{Add, AddAssign, Mul};
#[derive(Debug, Clone, Copy, PartialEq)]
struct Vector2 {
x: f64,
y: f64,
}
impl Add for Vector2 {
type Output = Vector2;
fn add(self, other: Vector2) -> Vector2 {
Vector2 { x: self.x + other.x, y: self.y + other.y }
}
}
impl Mul<f64> for Vector2 {
type Output = Vector2;
fn mul(self, k: f64) -> Vector2 {
Vector2 { x: self.x * k, y: self.y * k }
}
}
impl AddAssign for Vector2 {
fn add_assign(&mut self, other: Vector2) {
self.x += other.x;
self.y += other.y;
}
}
fn main() {
let mut pos = Vector2 { x: 1.0, y: 2.0 };
let vel = Vector2 { x: 0.5, y: -1.0 };
println!("{:?}", pos + vel);
println!("{:?}", vel * 4.0);
pos += vel;
pos += vel;
println!("{:?}", pos);
}
Vector2 { x: 1.5, y: 1.0 }
Vector2 { x: 2.0, y: -4.0 }
Vector2 { x: 2.0, y: 0.0 }
Piece by piece:
impl Add for Vector2means "Vector2 + Vector2".type Output = Vector2;says what+produces (it doesn't have to be the same type).fn add(self, other: Vector2)takes both sides by value. That's whyVector2derivesCopy:pos + velcopies them instead of moving them away.impl Mul<f64> for Vector2is "Vector2 * f64": the type in<>is the right-hand side. (ForAddit defaults to the same type, soimpl AddmeansAdd<Vector2>.)AddAssignis+=. It takes&mut selfand changes the value in place, so there's noOutput.
| Operator | Trait | Operator | Trait |
|---|---|---|---|
a + b |
Add |
a += b |
AddAssign |
a - b |
Sub |
a -= b |
SubAssign |
a * b |
Mul |
a / b |
Div |
-a |
Neg |
!a |
Not |
a == b |
PartialEq |
a < b |
PartialOrd |
a[i] |
Index |
a[i] = x |
IndexMut |
Neg is the unary minus: fn neg(self) -> Self::Output, with only one operand.
Index: square brackets on your own types
Index makes value[i] work. It returns a reference to something inside:
use std::ops::Index;
struct Playlist {
songs: Vec<String>,
}
impl Index<usize> for Playlist {
type Output = String;
fn index(&self, i: usize) -> &String {
&self.songs[i]
}
}
fn main() {
let p = Playlist { songs: vec![String::from("Crab Rave"), String::from("Ferris Wheel")] };
println!("{}", p[1]);
}
Ferris Wheel
The index type doesn't have to be a number: HashMap implements Index<&K>, so scores["ana"] works (and panics if the key is missing, like an out-of-bounds index).
Workplace note
Overload an operator only when its meaning is obvious to every reader: + on money or vectors, yes; + to "add a user to a group", no (write a method). Teams usually implement the full set together (Add, Sub, AddAssign...), so the type isn't surprising to use.
Common mistakes
- Forgetting
type Outputgiveserror[E0046]: not all trait items implemented, missing: `Output`. - A non-
Copytype is moved by+. Withstruct Money { cents: i64 }(noCopy),let c = a + b;movesaandb, and usingaafterwards iserror[E0382]: borrow of moved value: `a`. DeriveCopyfor small value types. - The operands in the other order.
impl Mul<f64> for Vector2allowsv * 2.0but not2.0 * v:
use std::ops::Mul;
#[derive(Debug, Clone, Copy)]
struct Vector2 {
x: f64,
y: f64,
}
impl Mul<f64> for Vector2 {
type Output = Vector2;
fn mul(self, k: f64) -> Vector2 {
Vector2 { x: self.x * k, y: self.y * k }
}
}
fn main() {
let v = Vector2 { x: 1.0, y: 2.0 };
println!("{:?}", 2.0 * v);
}
error[E0277]: cannot multiply `{float}` by `Vector2`
For that you'd also write impl Mul<Vector2> for f64.
Your task
Add is already done. Implement Sub (a - b) and Neg (-a) for Vector2.
Challenge 2
Make Money (a whole number of cents) work with +, += and * by a quantity. Implement Add (Money + Money), AddAssign (total += price) and Mul<i64> (Money * quantity).
Challenge 3
A game board stores its cells in one Vec<char>, row after row. Implement Index<(usize, usize)> for Grid so grid[(row, col)] returns the cell at that row and column. The cell's position in cells is row * width + col.
Previous: FromStr: parse into your own type Next: Ordering: PartialOrd and Ord for sorting