Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overloading the Add-operator without copying the operands

Tags:

rust

I'm writing an application in Rust that will have to use vector arithmetic intensively and I stumbled upon a problem of designing operator overload for a structure type.

So I have a vector structure like that:

struct Vector3d {
    pub x: f64,
    pub y: f64,
    pub z: f64,
}

and I want to be able to write something like that:

let x = Vector3d {x:  1.0, y: 0.0, z: 0.0};
let y = Vector3d {x: -1.0, y: 0.0, z: 0.0};

let u = x + y;

As far as I can see, there are three different ways to do it:

  1. Implement std::ops::Add trait for Vector3d directly. That works, but this trait's method signature is:

    fn add(self, other: Vector3d)
    

So it will invalidate its arguments after usage (because it moves them) which is undesirable in my case since many vectors will be used in multiple expressions.

  1. Implement Add trait for Vector3d and also implement the Copy trait. This works, but I feel iffy on that since Vector3d isn't exactly a lightweight thing (24 bytes at least) that can be copied quickly, especially when there are many calls to arithmetic functions.

  2. Implement Add for references to Vector3d, as suggested here. This works, but in order to apply the operator, I will have to write

    let u = &x + &y;
    

I don't like this notation because it doesn't exactly looks like its mathematic equivalent, just u = x + y.

I'm not sure which variant is optimal. So, the question is: is there a way to overload the '+' operator in such a way that

  1. It accepts its arguments as references instead of copying or moving them;
  2. It allows to write just u = x + y instead of u = &x + &y?
like image 933
Month Avatar asked Oct 28 '16 09:10

Month


1 Answers

Is there a way to overload the '+' operator in such a way that

  1. It accepts its arguments as references instead of copying or moving them;
  2. It allows to write just u = x + y instead of u = &x + &y?

No, there is no way to do that. Rust greatly values explicitness and hardly converts between types automatically.

However, the solution to your problem is simple: just #[derive(Copy)]. I can assure you that 24 bytes are not a lot. Computers these days love to crunch a lot of data at once instead of working on little chunks of data.


Apart from that, Copy is not really about the performance overhead of copying/cloning:

Types that can be copied by simply copying bits (i.e. memcpy).

And later in the documentation:

Generally speaking, if your type can implement Copy, it should.

Your type Vector3d can be copied by just copying bits, so it should implement Copy (by just #[derive()]ing it).

The performance overhead is a different question. If you have a type that can (and thus does) implement Copy, but you still think the type is too big (again: 24 bytes aren't!), you should design all your methods in a way that they accept references (it's not that easy; please read Matthieu's comment). This also includes the Add impl. And if you want to pass something to a function by reference, the programmer shall explicitly write it. That's what Rust's philosophy would say anyway.

like image 185
Lukas Kalbertodt Avatar answered Sep 18 '22 00:09

Lukas Kalbertodt