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.