5

I want to write some generic math functions, without assuming that my types are copyable. This seems not to be possible, since math operators consume the values, instead of borrowing them. So it is required to copy the object, just for simple math. I could also just move them, but this is not possible in borrowed context, which I need, when I want to mutate a struct.

Here some simple example, where are problems:

use std::ops::Add;

struct NoCopy<T>(T); //some non-copyable struct, (maybe a vector)
struct Cont<T>(NoCopy<T>); //some struct contaioning a noncopyable struct

impl<T: Add<Output=T>> Add for NoCopy<T> {
    type Output = NoCopy<T>;
    fn add(self, x: NoCopy<T>) -> NoCopy<T> {
        NoCopy(self.0+x.0)
    }
}

fn main() {
    let x = NoCopy(1);
    let cont = Cont(x);
    let rel = NoCopy(2);
    cont.0=cont.0+rel; //addition makes struct cont invalid, so i have to copy
}

And when I just want to calculate something with a not copyable object of a struct (for example the length of a vector), it won't work, since the value will either be consumed, so the struct gets invalid, or the borrow checker says "cannot move out of borrowed context". How would I correctly use a generic math, when mutating structs? Is it only possible for copyable types (or explicit cloning)?

Chris Emerson
  • 13,041
  • 3
  • 44
  • 66
porky11
  • 604
  • 5
  • 16
  • I'm not entirely sure on the question, but my first thought is that `Cont` itself doesn't have the Add operator — only the `NoCopy` objects — even thought `Cont` is what you're adding to a `NoCopy`? Another option could be making `cont` mutable and having `cont = Cont(cont.0 + rel)`? – Liam Gray Feb 22 '17 at 23:34
  • This is just an example. In real, this way may be problematic, since I have many fields in my struct, and also want to use it in borrowed contexts – porky11 Feb 23 '17 at 00:17

1 Answers1

9

One option is to implement Add on references:

impl<'a, T: Copy + Add<Output = T>> Add for &'a NoCopy<T> {
    type Output = NoCopy<T>;
    fn add(self, rhs: Self) -> NoCopy<T> {
        NoCopy(self.0+rhs.0)
    }
}

This is ok since shared references are Copy. Note I added a Copy constraint to T to make this implementation simple (true for integers); if T isn't copy then the one-liner method might need to change.

You do have to add references when using it, which is a shame, but it otherwise works:

fn main() {
    let x = NoCopy(1);
    let mut cont = Cont(x);
    let rel = NoCopy(2);
    cont.0=&cont.0+&rel;
}

(Playground)

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
Chris Emerson
  • 13,041
  • 3
  • 44
  • 66