9

Compiling the following Rust code that uses operator overloading

use std::ops::{Add};

#[derive(Show)]
struct Point {
    x: int,
    y: int
}

impl Add for Point {
    type Output = Point;

    fn add(self, other: Point) -> Point {
        Point {x: self.x + other.x, y: self.y + other.y}
    }
}

fn main() {
    let p: Point = Point {x: 1, y: 0};
    let pp = p + p;
}

Results in compiler errors due to ownership of p:

<anon>:21:18: 21:19 error: use of moved value: `p`
<anon>:21     let pp = p + p;
                           ^
<anon>:21:14: 21:15 note: `p` moved here because it has type `Point`, which is non-copyable
<anon>:21     let pp = p + p;
                       ^

The rationale behind it is explained here and led to an RFC that was not accepted (part of due to reasons of the above example). However, later the following RFC still introduced the by-value type signatures for operators.

While I understand the rationale behind the decision. Due to my lack of experience in rust, I'm not sure what the "proper" way would be to allow the above code to work (a) if I do not want to copy or (b) how to make the struct copyable?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
zgerd
  • 1,080
  • 8
  • 17

2 Answers2

6

If you don't want to copy then, as far as my newbie understanding goes, you need to implement Add on references to Point.

This would be supported by the RFC:

Fortunately, there is no loss in expressiveness, since you can always implement the trait on reference types. However, for types that do need to be taken by reference, there is a slight loss in ergonomics since you may need to explicitly borrow the operands with &. The upside is that the ownership semantics become clearer: they more closely resemble normal function arguments.

And indeed it seems to work:

use std::ops::{Add};

#[derive(Show)]
struct Point {
    x: i32,
    y: i32
}

impl<'a> Add for &'a Point {
    type Output = Point;

    fn add(self, other: &'a Point) -> Point { //'
        Point {x: self.x + other.x, y: self.y + other.y}
    }
}

fn main() {
    let p: Point = Point {x: 1, y: 0};
    let pp = &p + &p;
    println!("{:?}", pp);
}

(playpen)

To make Point copyable instead, just replace #[derive(Show)] with #[derive(Show,Copy)]. Such structs used to be copyable by default, but it changed.

Michał Politowski
  • 4,288
  • 3
  • 30
  • 41
  • 1
    The problem with this is that `let pp = &p + &p + &p` is not working. – SirVer Jan 15 '15 at 09:43
  • @SirVer yes, you would have to write something like `let pp = &(&p + &p) + &p`. I guess that the practical thing to do would be to create several implementations, like the answer by Vladimir Matveev suggests (or just derive `Copy` and be done with it). – Michał Politowski Jan 15 '15 at 11:31
5

If your structure can't be copied (e.g. it has Drop implementation, either itself or for one of its fields), then it may make sense to create several implementations: value+value, value+reference, reference+value and reference+reference. The first three can reuse the storage of one of the operands, and the last one can clone one of the operands and then just delegate to the already existing implementations. This way the user of your library can easily decide whether they want to reuse existing values for optimization or not.

In fact, that's how e.g. BigInt or Complex types are handled.

Your Point, however, can just be made Copy as it is cheap to copy it.

Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
  • Vladimir, thanks for your reply. What if my type is easy, but expensive to copy, say a `Matrix1000x1000` type? Is the compiler smart enough to avoid copying in chained operations or do I need to write the trivially forwards to the 4 implementations you mentioned? – SirVer Jan 16 '15 at 18:12
  • 1
    I'm not sure I understand your question. If your type is expensive to copy, don't make it `Copy` and implement operation traits for four variants (self+self, &self+self, self+&self, &self+&self). If your type is that large, you will need to put the data on the heap anyway, so move semantics will make sure that only the structure itself is copied. – Vladimir Matveev Jan 16 '15 at 18:25