Help rust newbie understand move with operator overloading

I have a vec3 struct which overloads several common operators:

struct vec3 {
    x: f32,
    y: f32,
    z: f32,
}
impl ops::Add for vec3 {
    type Output = Self;
    fn add(self, rhs:Self) -> Self {
        Self {
            x: self.x+rhs.x,
            y: self.y+rhs.y,
            z: self.z+rhs.z,
        }
    }
}

impl ops::Mul<f32> for vec3 {
    fn mul(self, rhs:f32) -> Self {
        Self {
            x: self.x * rhs,
            y: self.y * rhs,
            z: self.z * rhs,
        }
    }
}

And I have usage:

// let u = some vec3;
// let v = some vec3;
for i in 0..some_range {
    // let f = some float;
    // let g = some float;
    let t = v * f + u * g; // <-- error
    //...
}

This code can't compile because compiler think it's use of moved value for u and v

value moved here, in previous iteration of loop

What I don't understand is in my overloaded operators, Add and Mul will both return a new vec3, so my understanding is every time I call v * f + u * g it will move the newly created vec3 instead of u and v. Apparently I'm wrong so hope someone can help me understand what happened here.

PS: Is this because self is passed to overloaded operators and the member of self is used here to create new vec3 so self got moved?

The easiest solution is to #[derive(Clone, Copy)] on your struct vec3 (Playground).

The other option is to also overload the arithmetic operations on &vec3
(e.g., impl ops::Mul<f32> for &'_ Vec3).

And then the operation would be written: let t = &v * f + &u * g;

2 Likes

Also new to rust, but your example is essentially:

use std::ops;

struct Vec3 {
    x: f32,
    y: f32,
    z: f32,
}
impl ops::Add for Vec3 {
    type Output = Self;
    fn add(self, rhs:Self) -> Self {
        Self {
            x: self.x+rhs.x,
            y: self.y+rhs.y,
            z: self.z+rhs.z,
        }
    }
}

impl ops::Mul<f32> for Vec3 {
    type Output = Self;
    fn mul(self, rhs:f32) -> Self {
        Self {
            x: self.x * rhs,
            y: self.y * rhs,
            z: self.z * rhs,
        }
    }
}

fn main() {
    let u = Vec3 { x: 1.0, y: 1.0, z: 1.0 };
    let v = Vec3 { x: 2.0, y: 2.0, z: 2.0 };
    let things = vec![(1.1, 2.2), (3.3, 4.4)];
    for (f, g) in things {
        let t = v * f + u * g;
        dbg!((t.x, t.y, t.z));
    }
}

Which gives these errors on compile:

error[E0382]: use of moved value: `v`
  --> tmp.rs:35:17
   |
32 |     let v = Vec3 { x: 2.0, y: 2.0, z: 2.0 };
   |         - move occurs because `v` has type `Vec3`, which does not implement the `Copy` trait
...
35 |         let t = v * f + u * g;
   |                 ^ value moved here, in previous iteration of loop

error[E0382]: use of moved value: `u`
  --> tmp.rs:35:25
   |
31 |     let u = Vec3 { x: 1.0, y: 1.0, z: 1.0 };
   |         - move occurs because `u` has type `Vec3`, which does not implement the `Copy` trait
...
35 |         let t = v * f + u * g;
   |                         ^ value moved here, in previous iteration of loop

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0382`.

That's because the self in the trait calls are moving the value into the function, you probably wanted to take by reference instead, I.E. adding traits to handle the references as well:

use std::ops;

struct Vec3 {
    x: f32,
    y: f32,
    z: f32,
}

impl ops::Add for Vec3 {
    type Output = Vec3;
    fn add(self, rhs:Self) -> Vec3 {
        Vec3 {
            x: self.x+rhs.x,
            y: self.y+rhs.y,
            z: self.z+rhs.z,
        }
    }
}

impl ops::Add for &Vec3 {
    type Output = Vec3;
    fn add(self, rhs:Self) -> Vec3 {
        Vec3 {
            x: self.x+rhs.x,
            y: self.y+rhs.y,
            z: self.z+rhs.z,
        }
    }
}

impl ops::Mul<f32> for Vec3 {
    type Output = Vec3;
    fn mul(self, rhs:f32) -> Vec3 {
        Vec3 {
            x: self.x * rhs,
            y: self.y * rhs,
            z: self.z * rhs,
        }
    }
}


impl ops::Mul<f32> for &Vec3 {
    type Output = Vec3;
    fn mul(self, rhs:f32) -> Vec3 {
        Vec3 {
            x: self.x * rhs,
            y: self.y * rhs,
            z: self.z * rhs,
        }
    }
}

fn main() {
    let u = Vec3 { x: 1.0, y: 1.0, z: 1.0 };
    let v = Vec3 { x: 2.0, y: 2.0, z: 2.0 };
    let things = vec![(1.1, 2.2), (3.3, 4.4)];
    for (f, g) in things {
        let t = &v * f + &u * g;
        dbg!((t.x, t.y, t.z));
    }
}

I've no clue if this is the most succinct way, I highly doubt it, internal rust uses a macro to do it all at once:
https://doc.rust-lang.org/src/core/internal_macros.rs.html#23

Though as @Yandros stated, just deriving Copy for such a simple structure should be perfectly fine here and much easier so it gets copied in instead of moved.

2 Likes

Thanks guys! I think I will derive clone and copy on my vec3 struct.

1 Like