Skip to content

Latest commit

 

History

History
206 lines (147 loc) · 4.21 KB

implement-add-and-multiple-for-a-struct.md

File metadata and controls

206 lines (147 loc) · 4.21 KB
title timestamp author published description tags todo
Implement add and multiply for a struct in Rust
2024-02-10 04:40:01 -0800
szabgab
true
We can define the basic mathematical operations for structs as well.
impl
self
Self
Add
Mul
Output
implement matrix multiplication

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

{% include file="examples/2d-vectors/src/main.rs" %}

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 }