Implement add and multiply for a struct in Rust

impl self Self Add Mul Output

How to define the basic mathematical operations for structs in Rust?

Define Vector struct and create an instance of it

We can defined a 2 dimensional vector by the two coordinates it points to:

struct Vector {
    x: i32,
    y: i32,
}

We can then create an instance of this Vector:

let a = Vector {
    x: 2,
    y: 3,
};

Print the struct - derive from Debug

We cannot print the Vector using this println!("a: {}",a); as Display is not implemented by default.

We cannot even use the debugging print println!("a: {:?}",a); because Debug is not implemented by default.

We can implement both by ourselves, but it is easier to just derive the Debug trait. So for this we'll have

#[derive(Debug)]
struct Vector {
    x: i32,
    y: i32,
}

and then we can use

println!("a: {:?}",a);

or

dbg!(&a);

However, Rust will complain that we have not used the x and y attributes of the vector. In real code we will probably access those and we won't need to silence this warning, but here we do. So let's allow for dead code using #[allow(dead_code)]:

#[derive(Debug)]
#[allow(dead_code)]
struct Vector {
    x: i32,
    y: i32,
}

We can use the debugging printing in three different ways:

println!("a: {:?}",a);
println!("a: {a:?}");
dbg!(&a);

Adding two structs together

Given two vectors:

let a = Vector {
    x: 2,
    y: 3,
};

let b = Vector {
    x: 4,
    y: 5,
};

We can try to add them together:

let c = a + b;

This won't compile as Rust does not know how to add two arbitrary structs together. Rust will complain: must implement Add.

No need to worry, we can implement the Add trait.

impl Add for Vector {
    type Output = Self;

    fn add(self, other: Self) -> Self {
        Self {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

The lowercase self will be the variable that is on the left-hand-side of the operation and the other will be on the right-hand-side. This other is expected to be the Vector type which is represented here by Self (with capital S).

The add function returns a value of the same type (again using Self the generic name instead of the specific Vector,

Now we can have:

let c = a + b;
println!("c: {c:?}");

Multiply two structs

In the case of these vectors multiplying one such vector by another vector does not make much sense so I won't implement it.

We can have another example where we implement matrix-multiplication on them, but this isn't that article.

Multiply a struct with a number

This seemed to make more sense. The idea here is to multiply each coordinate by the same number.

We would like to be able to write code like this, where on the left-hand-side of the operator there is a Vector and the right-hand-side is an i32 number.

let d = c * 2;
println!("d: {d:?}");

For this to work we need to implement the Mul trait:

impl Mul<i32> for Vector {
    type Output = Self;

    fn mul(self, rhs: i32) -> Self::Output {
        Self { x: self.x * rhs, y: self.y * rhs }
    }
}

However this will only allow c * 2 it won't handle the 2 * c case. For that we need to implement that too:

Instead of duplicating all the "calculation", this implementation swaps the two operands and make the code reuse the above implementation.

impl Mul<Vector> for i32 {
    type Output = Vector;

    fn mul(self, rhs: Vector) -> Self::Output {
        rhs * self
    }
}

Full example

examples/2d-vectors/src/main.rs

use std::ops::Add;
use std::ops::Mul;

#[derive(Debug)]
#[allow(dead_code)]
struct Vector {
    x: i32,
    y: i32,
}

impl Add for Vector {
    type Output = Self;

    fn add(self, other: Self) -> Self {
        Self {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

impl Mul<i32> for Vector {
    type Output = Self;

    fn mul(self, rhs: i32) -> Self::Output {
        Self {
            x: self.x * rhs,
            y: self.y * rhs,
        }
    }
}

impl Mul<Vector> for i32 {
    type Output = Vector;

    fn mul(self, rhs: Vector) -> Self::Output {
        rhs * self
    }
}

fn main() {
    let a = Vector { x: 2, y: 3 };

    let b = Vector { x: 4, y: 5 };

    // We need to derive from Debug to make this printable
    println!("a: {:?}", a);
    println!("b: {b:?}");
    dbg!(&a);

    // We need to implement the [Add trait](https://doc.rust-lang.org/std/ops/trait.Add.html)
    let c = a + b;
    println!("c: {c:?}");

    // // We need to implement the [Mul trait](https://doc.rust-lang.org/std/ops/trait.Mul.html)
    let d = c * 2;
    println!("d: {d:?}");

    let e = 2 * d;
    println!("e: {e:?}");
}

Output

a: Vector { x: 2, y: 3 }
b: Vector { x: 4, y: 5 }
[src/main.rs:55:5] &a = Vector {
    x: 2,
    y: 3,
}
c: Vector { x: 6, y: 8 }
d: Vector { x: 12, y: 16 }
e: Vector { x: 24, y: 32 }

Author

Gabor Szabo (szabgab)

Gabor Szabo, the author of the Rust Maven web site maintains several Open source projects in Rust and while he still feels he has tons of new things to learn about Rust he already offers training courses in Rust and still teaches Python, Perl, git, GitHub, GitLab, CI, and testing.

Gabor Szabo