Operator overloading and Generics

Hi Rustaceans,

For trait and generic exploration, I'm writing a basic Vector2D code. The struct is quite simple:

#[derive(Debug, Copy, Clone)]
pub struct Vector2D<T> {
    pub x: T,
    pub y: T,
}

I managed to implement Add, Sub and Mul *T operation (vector * T) , but I'm unable to implement T * vector . Here is my implementation for Vec * T operation:

impl<T: Copy + std::ops::Mul<Output=T>> ops::Mul<T> for Vector2D<T> {
    type Output = Vector2D<T>;

    fn mul(self, scale: T) -> Self::Output {
        Vector2D { x: self.x * scale, y: self.y * scale }
    }
}

I tried many things, but the best I can do is:

impl<T: Copy + std::ops::Mul<Output=T>> ops::Mul< Vector2D<T>>  for T {
    type Output = Vector2D<T>;

    fn mul(self, v: Vector2D<T>) -> Self::Output {
        Vector2D { x: self * v.x, y: self  * v.y }
    }
}


which leads to the following error:

error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`vector::Vector2D<T>`)
  --> src/vector.rs:87:6
   |
87 | impl<T: Copy + std::ops::Mul<Output=T>> ops::Mul< Vector2D<T>>  for T {
   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`vector::Vector2D<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

I spent too much time on this, and now I'm really confused. The error message make me think to something related to the ´Orphan Rule', but I'm not sure about that. Now, but I'm asking if I'm not trying to do something impossible ?

Best regards

Yup, that's the orphan rule(s). Basically some other type Foo could implement impl<U> Mul<U> for Foo, which would overlap with your impl<T> Mul<Vector2D<T>> for T in case of T == Foo and U == Vector2D<Foo>> (for simplicity, I'm ignoring the additional trait bounds of your implementation). In order to avoid such overlap, the orphan rules do (somewhat arbitrarily) choose to allow the former but disallow the latter implementation.

So ultimately there truly is no way to write this implementation. The perhaps “best” thing you can do is to implement Mul<Vector2D<u32> for u32, and Mul<Vector2D<f32>> for f32 and so on, for a finite list of useful types (doing these impls neatly could be done with a macro); or you could choose not to support the operator at all this way around.

2 Likes

Thanks for your fast answer, it save me a lot of time :slight_smile :slight_smile:
I will choose the last option, not to support the operator at all.

Regards

Sorry to butt-in on this discussion, but does it mean that it is impossible to generically implement the Mul trait for Scalar * Vector<Scalar> and Vector<Scalar> * Scalar operations ?

Yes, in particular Scalar * Vector<Scalar> doesn’t work with a fully generic Scalar type, but you can do it for concrete Scalar types. OTOH, Vector<Scalar> * Scalar is not a problem.

You can also observe this in existing crates such as ndarray or nalgebra which do list explicit implementations for u8/u16/u32/u64/i8/i16/i32/i64/f32/f64 (and in the case of ndarray also for bool and u128/i128) to support scalars on the left-hand-side of multiplication (or also addition in case of ndarray), while a single generic implementation can be used for the same operation the other way around.

use nalgebra::vector;

fn main() {
    let x = vector![1, 2, 3];

    let y = x * 10;

    dbg!(y);
    
    // works for i32

    let y1 = 10 * x;

    dbg!(y1);

    // -----
    
    generic(1, 2, 3, 10);
}

use std::fmt::Debug;
use std::ops::Mul;
use std::ops::MulAssign;

fn generic<T>(a: T, b: T, c: T, s: T)
where
    T: Mul<T, Output = T> + Copy + Debug, // useful or necessary for this use-case
    T: 'static + PartialEq + MulAssign<T>, // also required by the `Mul` implementation
{
    let x = vector![a, b, c];

    let y = x * s;

    dbg!(y);

    // wouldn't work this way:
    //
    // let y1 = s * x;
    //
    // dbg!(y1);
}

Rust Playground

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.