Search code examples
rustoperator-overloadingtraits

How can I implement a foreign trait for interoperation between foreign primitives under a local trait and local structs


Background

I am trying to make a library that can be used with various primitive numeric types, but I'm having some trouble getting some of the overloads to work. (see the attached playground for a minimal code example)

See E0210 and E0117

The problem

I can see that the orphan rules for rust are being violated by my code example, but I don't understand why this is considered a 'bad thing' in this case.

It seems like making a plus operator would allow one to create one that respects the commutative rule, but due to the inherently ordered nature of operator overloads, am I simply not able to?

Desired outcome

I would like to find a way to get a reversed plus operator overload (ie allow MyType + i32 and i32 + MyType to both be valid and both return MyType. If this is not possible then I will just have to make that concession.

What I've tried

This post on stack overflow seems to explain the issue pretty well and offer a workaround that sort of worked (in that it allowed the overload to compile, but didn't allow the reversed operator overload to function correctly)

I saw here that it's not possible to work around this issue, but I'm neither confident in nor surprised at this conclusion.

Example Code

#![allow(unused)]
use std::ops;

struct MyType {
    val: i32,
}

trait SupportedType<T> {
    fn as_my_type(val: T) -> MyType;
}

impl SupportedType<i32> for i32 {
    fn as_my_type(val: Self) -> MyType {
        MyType { val: val }
    }
}

impl ops::Add<MyType> for MyType {
    type Output = MyType;
    
    fn add(self, val: MyType) -> Self::Output {
        MyType { val: self.val + val.val }
    }
}

// Works
impl<T: SupportedType<T>> ops::Add<T> for MyType where T: SupportedType<T> {
    type Output = MyType;
    
    fn add(self: MyType, val: T) -> Self::Output {
        self + T::as_my_type(val)
    }
}

// Doesn't work
impl<T: SupportedType<T>> ops::Add<MyType> for T where T: SupportedType<T> {
    type Output = MyType;
    
    fn add(self, val: T) -> Self::Output {
        self + T::as_my_type(val)
    }
}

fn main() {
    let my_val: MyType = MyType { val: 42 };
    let the_chosen_one: i32 = 21;
    
    // Works: uses the first one
    assert_eq!((my_val + the_chosen_one).val, 63);
    // Doesn't work: uses the second one
    assert_eq!((the_chosen_one + my_val).val, 63);
}

Solution

  • You can do that only with concrete types. Doing that generically, as you've tried, is impossible.