I think this is unfortunately impossible. Someone could go through the effort of implementing Float
for their own type, e.g.
use std::num::FpCategory;
use std::ops::*;
use num::{One, Zero, Num, Float, NumCast, ToPrimitive};
#[derive(Clone, Copy, PartialOrd, PartialEq)]
struct MyFloat(f32);
impl One for MyFloat {
fn one() -> Self { Self(1.0) }
}
impl Zero for MyFloat {
fn zero() -> Self { Self(0.0) }
fn is_zero(&self) -> bool { self.0.is_zero() }
}
impl Add for MyFloat {
type Output = Self;
fn add(self, other: Self) -> Self { Self(self.0 + other.0) }
}
impl Sub for MyFloat {
type Output = Self;
fn sub(self, other: Self) -> Self { Self(self.0 - other.0) }
}
/*
impl Mul for MyFloat {
type Output = Self;
fn mul(self, other: Self) -> Self { Self(self.0 * other.0) }
}
*/
impl Rem for MyFloat {
type Output = Self;
fn rem(self, other: Self) -> Self { Self(self.0 % other.0) }
}
impl Div for MyFloat {
type Output = Self;
fn div(self, other: Self) -> Self { Self(self.0 / other.0) }
}
impl Num for MyFloat {
type FromStrRadixErr = <f32 as Num>::FromStrRadixErr;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
Ok(Self(f32::from_str_radix(str, radix)?))
}
}
impl Neg for MyFloat {
type Output = Self;
fn neg(self) -> Self { Self(-self.0) }
}
impl Float for MyFloat {
fn nan() -> Self { Self(f32::nan()) }
fn infinity() -> Self { Self(f32::infinity()) }
fn neg_infinity() -> Self { Self(f32::neg_infinity()) }
fn neg_zero() -> Self { Self(f32::neg_zero()) }
fn min_value() -> Self { Self(f32::min_value()) }
fn min_positive_value() -> Self { Self(f32::min_positive_value()) }
fn max_value() -> Self { Self(f32::max_value()) }
fn is_nan(self) -> bool { self.0.is_nan() }
fn is_infinite(self) -> bool { self.0.is_infinite() }
fn is_finite(self) -> bool { self.0.is_finite() }
fn is_normal(self) -> bool { self.0.is_normal() }
fn classify(self) -> FpCategory { self.0.classify() }
fn floor(self) -> Self { Self(self.0.floor()) }
fn ceil(self) -> Self { Self(self.0.ceil()) }
fn round(self) -> Self { Self(self.0.round()) }
fn trunc(self) -> Self { Self(self.0.trunc()) }
fn fract(self) -> Self { Self(self.0.fract()) }
fn abs(self) -> Self { Self(self.0.abs()) }
fn signum(self) -> Self { Self(self.0.signum()) }
fn is_sign_positive(self) -> bool { self.0.is_sign_positive() }
fn is_sign_negative(self) -> bool { self.0.is_sign_negative() }
fn mul_add(self, a: Self, b: Self) -> Self { Self(self.0.mul_add(a.0, b.0)) }
fn recip(self) -> Self { Self(self.0.recip()) }
fn powi(self, n: i32) -> Self { Self(self.0.powi(n)) }
fn powf(self, n: Self) -> Self { Self(self.0.powf(n.0)) }
fn sqrt(self) -> Self { Self(self.0.sqrt()) }
fn exp(self) -> Self { Self(self.0.exp()) }
fn exp2(self) -> Self { Self(self.0.exp2()) }
fn ln(self) -> Self { Self(self.0.ln()) }
fn log(self, base: Self) -> Self { Self(self.0.log(base.0)) }
fn log2(self) -> Self { Self(self.0.log2()) }
fn log10(self) -> Self { Self(self.0.log10()) }
fn max(self, other: Self) -> Self { Self(self.0.max(other.0)) }
fn min(self, other: Self) -> Self { Self(self.0.min(other.0)) }
fn abs_sub(self, other: Self) -> Self { Self(Float::abs_sub(self.0, other.0)) }
fn cbrt(self) -> Self { Self(self.0.cbrt()) }
fn hypot(self, other: Self) -> Self { Self(self.0.hypot(other.0)) }
fn sin(self) -> Self { Self(self.0.sin()) }
fn cos(self) -> Self { Self(self.0.cos()) }
fn tan(self) -> Self { Self(self.0.tan()) }
fn asin(self) -> Self { Self(self.0.asin()) }
fn acos(self) -> Self { Self(self.0.acos()) }
fn atan(self) -> Self { Self(self.0.atan()) }
fn atan2(self, other: Self) -> Self { Self(self.0.atan2(other.0)) }
fn sin_cos(self) -> (Self, Self) { let (s, c) = self.0.sin_cos(); (Self(s), Self(c)) }
fn exp_m1(self) -> Self { Self(self.0.exp_m1()) }
fn ln_1p(self) -> Self { Self(self.0.ln_1p()) }
fn sinh(self) -> Self { Self(self.0.sinh()) }
fn cosh(self) -> Self { Self(self.0.cosh()) }
fn tanh(self) -> Self { Self(self.0.tanh()) }
fn asinh(self) -> Self { Self(self.0.asinh()) }
fn acosh(self) -> Self { Self(self.0.acosh()) }
fn atanh(self) -> Self { Self(self.0.atanh()) }
fn integer_decode(self) -> (u64, i16, i8) { self.0.integer_decode() }
}
impl NumCast for MyFloat {
fn from<T: ToPrimitive>(x: T) -> Option<Self> { Some(Self(<f32 as NumCast>::from(x)?)) }
}
impl ToPrimitive for MyFloat {
fn to_i64(&self) -> Option<i64> { self.0.to_i64() }
fn to_u64(&self) -> Option<u64> { self.0.to_u64() }
fn to_isize(&self) -> Option<isize> { self.0.to_isize() }
fn to_i8(&self) -> Option<i8> { self.0.to_i8() }
fn to_i16(&self) -> Option<i16> { self.0.to_i16() }
fn to_i32(&self) -> Option<i32> { self.0.to_i32() }
fn to_i128(&self) -> Option<i128> { self.0.to_i128() }
fn to_usize(&self) -> Option<usize> { self.0.to_usize() }
fn to_u8(&self) -> Option<u8> { self.0.to_u8() }
fn to_u16(&self) -> Option<u16> { self.0.to_u16() }
fn to_u32(&self) -> Option<u32> { self.0.to_u32() }
fn to_u128(&self) -> Option<u128> { self.0.to_u128() }
fn to_f32(&self) -> Option<f32> { self.0.to_f32() }
fn to_f64(&self) -> Option<f64> { self.0.to_f64() }
}
but then add a generic implementation like this
impl<T> Mul<T> for MyFloat {
type Output = MyFloat;
fn mul(self, _other: T) -> MyFloat {
self
}
}
This is the reasoning behind why you can’t write a
impl<T: Float> Mul<Vector3d<T>> for T {
type Output = Vector3d<T>;
fn mul(self, rhs: Self::Output) -> Self::Output {
Self::Output {x : rhs.x * self, y : rhs.y * self, z : rhs.z * self}
}
}
(by the way, note that you don’t need to add those Sub
, Add
, Mul
, and Div
bounds to the Float
bound since they are included in the Num
supertrait of Float
)
Rust solves the possible conflicts from such an implementation Mul<Vector3d<T>> for T
, as demonstrated above, by requiring, well, as the error message will explain:
error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Vector3d<T>`)
--> src/lib.rs:13:6
|
13 | impl<T: Float> Mul<Vector3d<T>> for T {
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Vector3d<T>`)
|
= note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
= note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
error: aborting due to previous error
or simply speaking: there is an arbitrary requirement that some plain variable T
can only be the right operand of Mul
but not the left. This way only one of the two conflicting implementations
impl<T: Float> Mul<Vector3d<T>> for T
and
impl<T> Mul<T> for MyFloat
are allowed.
So what can you do? Implement generic multiplication from the right:
impl<T: Float> Mul<T> for Vector3d<T> {
type Output = Vector3d<T>;
fn mul(self, rhs: T) -> Self::Output {
Self::Output {x : self.x * rhs, y : self.y * rhs, z : self.z * rhs}
}
}
and also add a manual list of implementations of multiplication from the left, so that at least f32
and f64
are supporting scalar * vector
notation:
macro_rules! left_multiplication {
($($T:ty),*) => {
$(
impl Mul<Vector3d<$T>> for $T {
type Output = Vector3d<$T>;
fn mul(self, rhs: Self::Output) -> Self::Output {
Self::Output {x : rhs.x * self, y : rhs.y * self, z : rhs.z * self}
}
}
)*
}
}
left_multiplication!{
f32, f64
}
Also note that this is basically the approach taken in e.g. the nalgebra
crate, see right multiplication here, and left multiplication starting here.