2

I have a struct with a couple of operators implemented for it:

use std::ops;

/// Vector of 3 floats
#[derive(Debug, Copy, Clone)]
pub struct Vec3 {
    pub x: f32,
    pub y: f32,
    pub z: f32,
}

/// Add operator
impl ops::Add<&Vec3> for &Vec3 {
    type Output = Vec3;

    #[inline(always)]
    fn add(self, rhs: &Vec3) -> Self::Output {
        Vec3 {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
            z: self.z + rhs.z,
        }
    }
}

/// Subtract operator
impl ops::Sub<&Vec3> for &Vec3 {
    type Output = Vec3;

    #[inline(always)]
    fn sub(self, rhs: &Vec3) -> Self::Output {
        Vec3 {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
            z: self.z - rhs.z,
        }
    }
}

/// Scalar multiplication operator
impl ops::Mul<&Vec3> for f32 {
    type Output = Vec3;

    #[inline(always)]
    fn mul(self, rhs: &Vec3) -> Self::Output {
        Vec3 {
            x: self * rhs.x,
            y: self * rhs.y,
            z: self * rhs.z,
        }
    }
}

I want to use the operators:

let a = Vec3 { x: 0.0, y: 0.5, z: 1.0 };
let b = Vec3 { x: 1.0, y: 0.5, z: 0.0 };
let c = Vec3 { x: 1.0, y: 1.0, z: 0.0 };
let d = Vec3 { x: 0.0, y: 1.0, z: 1.0 };
let result = 2.0 * (a + b) - 3.0 * (c - d);

This code will not compile because the operators are implemented for &Vec3, not for Vec3. To fix the issue, the last line would have to look like this:

let result = &(2.0 * &(&a + &b)) - &(3.0 * &(&c - &d));

Which doesn't look that nice anymore.

I understand that I could implement the operators for Vec3 to avoid that problem, but what if I still want to use immutable references to these vectors on the stack? Is there perhaps a way to give Rust some hint that if I write a + b and there is no operator for Vec3 + Vec3, that it could try and look for a &Vec3 + &Vec3 operator instead, and if found, take the immutable references for both arguments automatically?

Petr Broz
  • 8,891
  • 2
  • 15
  • 24
  • 1
    alternatively, you could have a,b,c,d as references to begin with. – Sergio Tulentsev May 26 '21 at 12:30
  • 1
    Thanks, that's true. The intermediate results of the `+`/`-`/`*` operations would still be `Vec3`s, though, so the line would look like `let result = &(2.0 * &(a_ref + b_ref)) - &(3.0 * &(c_ref - d_ref));` – Petr Broz May 26 '21 at 12:44
  • 3
    It is common to implement binary operators for both references and owned values, e.g. for `Vec3 + Vec3`, `&Vec3 + Vec3`, `Vec3 + &Vec3` and `&Vec3 + &Vec3`. This way, all intermediate results can be passed by value – it's fine to move them anyway. Together with making your variables references to begin with, this allows you to write the expression exactly in the form you want. – Sven Marnach May 26 '21 at 13:19

1 Answers1

5

No, there is no way of automatically taking a reference when adding two values.

You could write your own macro that does this, I suppose. In usage, it would look like:

thing!{ a + b }
// expands to
(&a + &b)

I'd expect that this macro would quickly become tiresome to write.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366