1
votes

As part of a "learn Rust" project, I've been working through some Project Euler problems, in which it would be convenient to have a few generic math functions. As an example, let's say I want to write a generic "square" function. If I can live with the builtin numeric types (all of which I believe are Copy), I can write this:

fn square<A>(n: A) -> A
where
    A: Mul<Output = A> + Copy,
{
    n.mul(n)
}

This seems to work fine. But what if I want to use a numeric type that is not Copy? Say I'm using a bignum library in which numbers are not Copy, but which implements std::ops::Mul. I would have thought I could do this:

fn square_ref<'a, A>(n: &'a A) -> A
where
    A: Mul<&'a A, Output = A>,
{
    n.mul(n)
}

but that gets me the following error:

error[E0507]: cannot move out of `*n` which is behind a shared reference
  --> src/main.rs:16:5
   |
16 |     n.mul(n)
   |     ^ move occurs because `*n` has type `A`, which does not implement the `Copy` trait

error: aborting due to previous error

Why does the call to mul insist on resolving to type A, instead of &A? Just to be clear, my question isn't really about generic math in Rust -- I'm hoping that figuring out how to do this sort of thing, or learning why it can't be done, will help me better understand the language in general.

1
Another option is to require Clone instead of Copy: play.rust-lang.org/… Primitive types will implement Clone the same as they do Copy, and bignums will have Clone. Also note that you don't need to write n.mul(n) in square and in square_ref, you can write n * n because * invokes the Mul trait.user4815162342
I would not advice to clone a bigint if you can avoid it, in fact, clone should always be avoid when possible.Stargateur
@Stargateur That's good general advice, but there are situations where clone() is quite appropriate, e.g. when implementing Mul in terms of MulAssign. It's important to be aware of the option to request Clone, which is the closest equivalent of Copy one has for all kinds of numbers (and which will be as performant as Copy on primitive nums).user4815162342
@user4815162342 I don't see what suddenly you talk about MulAssign.Stargateur
@Stargateur It's an example of where cloning numbers is put to good use. Binary arithmetic operations such as multiplication usually come in two forms, a destructive one (the *=) and a preserving one (*). One way to avoid duplication is to implement the more basic one, *=, and trivially provide * as let new = self.clone(); new *= other; new. In that case clone() is not something to avoid, but actually expresses the intent of the code.user4815162342

1 Answers

3
votes

You need to bound &'a A to be Mul, not A:

use std::ops::Mul;

fn square_ref<A>(n: &A) -> A
where
    // note the &'a A instead of just A
    for<'a> &'a A: Mul<&'a A, Output = A>,
{
    n.mul(n)
}