The bound T: Mul
does not imply that the result of the binary operator is also of type T
. The result type is an associated type of this trait: Output
.
The other issue is that before Rust 1.0 the operator traits switched from pass-by-reference to pass-by-value. In generic code this can be a bit of a pain in the butt (for now at least) because these operators consume their operands unless you also require the types to be Copy
.
Just for completeness (in case you don't like to require Copy
), let me add some information about a possible alternative direction.
For the sake of generic code, authors of "numeric types" are encouraged to provide additional non-consuming implementations of these operator traits so that you don't need Copy
or Clone
. For example, the standard library already provides the following implementations:
f64 implements Mul< f64>
f64 implements Mul<&f64>
&f64 implements Mul< f64>
&f64 implements Mul<&f64>
Each of these implementations has f64
as the Output
type. Making use of these traits directly is not pretty:
fn cube<T>(x: &T) -> T
where
for<'a> T: Mul<&'a T, Output = T>,
for<'a, 'b> &'a T: Mul<&'b T, Output = T>,
{
x * x * x
}
Eventually, we might get some (slightly) higher level traits, which would reduce the noise. For example: T: Mul2
could imply T: Mul<T> + Mul<&T>
and &T: Mul<T> + Mul<&T>
, but at the time of writing this, the Rust compiler does not seem able to handle this. At least I could not successfully compile the following code:
use std::ops::Mul;
pub trait Mul2
where
Self: Mul<Self, Output = Self>,
Self: for<'a> Mul<&'a Self, Output = Self>,
for<'a> &'a Self: Mul<Self, Output = Self>,
for<'a, 'b> &'a Self: Mul<&'b Self, Output = Self>,
{
}
impl<T> Mul2 for T
where
T: Mul<T, Output = T>,
T: for<'a> Mul<&'a T, Output = T>,
for<'a> &'a T: Mul<T, Output = T>,
for<'a, 'b> &'a T: Mul<&'b T, Output = T>,
{
}
fn cube<T: Mul2>(x: &T) -> T {
x * x * x
}
fn main() {
let c = cube(&2.3);
println!("Hello, world! {}", c)
}
I think it's safe to say that things will improve in this area. For now, the ability to generically implement numeric algorithms in Rust is not as good as I would like it to be.