0

I have a minus function which leads to a use of moved value: 'scalar' compile error, which makes sense.

struct Point<T> {
    x: T,
    y: T,
    z: T,
}

impl<T: Sub<Output = T>> Point<T> {
    //subtract scalar value from all Point fields.
    fn minus(self, scalar: T) -> Self {
        Point {
            x: self.x - scalar,
            y: self.y - scalar,
            z: self.z - scalar,
        }
    }
}

Solution 1

Make the type T cloneable. It fixes this but it seems like very expensive operation because it's copying the scalar value for each field. Is there another less expensive solution?

impl<T: Sub<Output = T> + Clone> Point<T> {
    fn minus(self, scalar: T) -> Self {
        Point {
            x: self.x - scalar.clone(),
            y: self.y - scalar.clone(),
            z: self.z - scalar.clone(),
        }
    }
}

Attempted solution 2

I thought maybe I can use references. But then I get cannot subtract '&T' from '&T', which I don't understand.

Is there a more efficient way to do this that doesn't Clone or Copy the input scalar value?

impl<T: Sub<Output=T>> Point<T> {
    fn minus(self, scalar: &T) -> Self {
        Point {
            x: &self.x - scalar,
            y: &self.y - scalar,
            z: &self.z - scalar,
        }
    }
}
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
user794783
  • 3,619
  • 7
  • 36
  • 58
  • 3
    The correct answer to this question highly depends on what kind of types you expect to use with `Point`. If they'll always be things like `f64`, `u32`, and such, then it's perfectly fine to "clone" those fields, since "cloning" is no-op for such types (it's copying a register-sized value that is easy optimized away). For tyypes like `BigInt`, cloning can be more expensive and `Copy` is unavailable. Writing generic numeric code in Rust is not easy without crates like `num` and requires you to decide early on what use cases you want to support. – user4815162342 Oct 17 '21 at 07:54
  • Oh so there is no other option other than cloning :-( I didn't know about `num`. I will look into it. – user794783 Oct 17 '21 at 08:03
  • 4
    It's important to realize that "cloning" in a generic context is almost meaningless, and isn't necessarily expensive at all. In particular, cloning a number is like assignment, there is nothing to it. Cloning only becomes expensive when dealing with heap-based types, and even then it's as expensive as creating a new value of the type. So if you're fine with creating a number to put in your `Point`, you should be fine with "cloning" it. – user4815162342 Oct 17 '21 at 08:25
  • 3
    For solution 2, you need to bound `&T` instead of `T` itself. See e.g. [How to write a trait bound for adding two references of a generic type?](/q/34630695) – trent Oct 17 '21 at 09:39
  • 1
    @user794783 Keep in mind the same C++ code would implicitly clone anything with a copy-constructor. Rust makes you aware of when you are coping or cloning values - this awareness is distinct from it being expensive. – GManNickG Oct 18 '21 at 00:49
  • I see there are 2 closes. It's weird that every time I post a Rust question as a beginner people are always frekkin ready to close it down. – user794783 Oct 18 '21 at 04:32
  • 1
    I didn't vote to close, but I think you should be aware that your question as posed is basically unanswerable. How is copying a scalar (i.e. a value like `u32` or `f64`) "expensive"? How would you even subtract the value without copying it to a CPU register? Without explaining your use case, your concerns are hard to address in a meaningful manner. – user4815162342 Oct 18 '21 at 20:24
  • I understand. Thank you for all the responses. Really helped! – user794783 Oct 20 '21 at 11:54

1 Answers1

1

To subtract without Copy or Clone you need to constrain that your elements can subtract by reference, i.e. &T - &T. You can do that by constraining with a higher ranked trait bound. (This is shown in the link provided by @trentcl)

impl<T> Point<T>
where
    for<'a> &'a T: Sub<Output = T>,
{
    fn minus(&self, scalar: &T) -> Self {
        Point {
            x: &self.x - scalar,
            y: &self.y - scalar,
            z: &self.z - scalar,
        }
    }
}

Implementations for mathematical operators should at least have by-reference available, even if by-value is more convenient and readable for basic Copy types.

Whether the Copy or Clone is expensive depends on what T is, but since this is a generic context, you should strive to avoid copying or cloning whenever possible so you can avoid costs in case it is expensive. And if not possible, Rust forces you to add the Copy/Clone constraint so callers know that the value may be copied and can make judgement whether that is appropriate.

kmdreko
  • 42,554
  • 6
  • 57
  • 106