How to implement Mul Trait for a custom struct type to work in both ways
Asked Answered
D

1

5
// main.rs
#[derive(Clone, Copy)]
struct A(f64, f64);
impl<T> Mul<T> for A
where
    f64: From<T>,
    T: Copy, // f64: Mul<T>,
{
    type Output = A;
    fn mul(mut self, rhs: T) -> Self::Output {
        self.0 = self.0 * f64::from(rhs);
        self.1 = self.1 * f64::from(rhs);
        self
    }
}

impl Mul<A> for i32 {
    type Output = A;
    fn mul(self, mut rhs: A) -> Self::Output {
        rhs.0 = rhs.0 * f64::from(self);
        rhs.1 = rhs.1 * f64::from(self);
        rhs
    }
}

fn main() {
    let mut a = A(1.0, 1.0);
    a = a * 2;             // is fine
    a = a * 2.0;           // is fine
    a = a * 1 as u8;       // is fine

    a = 2 * a;             // is fine because I did implement for i32 type
    a = 2.0 * a;           // impl this with generic type!
}

I'm able to implement Mul Trait for my struct A with generic parameter T ,

impl<T> Mul<T> for A
where
    f64: From<T>,
    T: Copy, 
{
    type Output = A;
    fn mul(mut self, rhs: T) -> Self::Output {
        self.0 = self.0 * f64::from(rhs);
        self.1 = self.1 * f64::from(rhs);
        self
    }
}

Now I can multiply A with any numeric type like

A * f64 or A * i32 etc

But I am unable to implement Mul Trait with generic parameter which make me to do this:

f64 * A and i32 * A

Is there any way to implement it like this

impl Mul<A> for i32 {
    type Output = A;
    fn mul(self, mut rhs: A) -> Self::Output {
        rhs.0 = rhs.0 * f64::from(self);
        rhs.1 = rhs.1 * f64::from(self);
        rhs
    }
}

But for all types (generic parameter)

impl<T> Mul<A> for T { // error:type parameter `T` must be covered by another type when it appears before the first local type
    type Output = A;
    fn mul(self, mut rhs: A) -> Self::Output {
        rhs.0 = rhs.0 * f64::from(self);
        rhs.1 = rhs.1 * f64::from(self);
        rhs
    }
}

Complete error:

error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`A`)
  --> src\main.rs:64:6
   |
64 | impl<T> Mul<A> for T {
   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`A`)
   |
   = 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

For more information about this error, try `rustc --explain E0210`.
Dioptometer answered 3/12, 2021 at 20:13 Comment(0)
S
7

You can't. You can only be generic for the right-hand argument.

How libraries generally solve this is implement it generically for Self * T, and then make a macro that implements T* Self in terms of Self * T explicitly substituting T for the list of types they support, e.g. as in nalgebra:

left_scalar_mul_impl!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, f32, f64);
Saguache answered 3/12, 2021 at 21:7 Comment(1)
And for a discussion on why it's not possible: rust-lang.github.io/rfcs/2451-re-rebalancing-coherence.htmlManducate

© 2022 - 2024 — McMap. All rights reserved.