Does Rust assumes a type overloading operator + should be copyable?


#1

The signature of trait Add’s method add is
fn add(self, rhs: RHS) -> Self::Output;

So that for both self and rhs, if Copy trait is not impled, the behavior would be rather strange, e.g., you cannot write
a+a
if a has a type that impls Add, but not Copy.

Is that right?

If yes, would it cause unnecessary copying in operator+?


#2

No, look at the docs.

As seen in the first example, it is perfectly valid to add two Points although they do not implement Clone or Copy.

Of course, if you want to be able to add something to itself in a standard way (i.e. not calling a new() function all the time), you would implement Clone or Copy.

TypeA::new(&a) + a     // Not sure why you would want this vs
a.clone() + a          // TypeA implements Clone or Copy

Or add an add_self() function if you want to avoid the copying (which should be easily optimizable anyways?)


#3

You can impl Add for references of the type and then do &a + &a. Typically addable types are clone and/or copy so this wouldn’t be necessary, but it’s possible.


#4

My expectation is definitely that Add types are Copy, and additionally that adding is “cheap”, but I guess that doesn’t have to be true in all cases.


#5

For scalar types like Complex, it is ok to assume Add types are Copy. But if some one want to implement a Matrix struct, and want to use operator+ to simulate the Matrix adding operation, that could be rather annoying.

We have to choose from:

  1. write &a+&b to gain a high performance, but ugly.
  2. write a+b, which is nature, but requires innecessay copying.

Is that right?


#6

You can implement all of the a+b, &a + b, a + &b, &a + &b.

Also a + b means, that addition implementation takes ownership of inputs. The implementation does not need to copy them, but can reuse them. Sadly if you do a + b, then you cannot use a again on caller side.
On the other hand, this might be super useful, if you chain operations like a + b*c. If you only have borrow based implementation then you would have to write &a + &(&b * &c) which is ugly as hell.

On the side note. You can use Borrow trait for implementing function which takes either ownership or borrow. It looks like this https://play.rust-lang.org/?gist=a3b79985aef4f4e3af66e998b46b954b&version=stable
But it does not work for Add trait, due to trait impl limitations (you would have to create your custom borrow trait).


#7

Thanks. Though verbose ( to implement Add for a+b, &a+b, a+&b, &a+&b), it is acceptable to me to write such things like:
let b=&a+(&a+&a);

This tells me that even though writing operator overloading codes, we must be aware of which variable is borrowed, whose ownership is being taken, etc.

Also I studied a little about the math operator overloading of the lib ndarray, it uses macro to implement math operators.


#8

Just for completeness, the arithmetic ops doc for ndarray is at: ArrayBase#Arithmetic Operations