Promotion / Conversion on Generic Types

Suppose I have a very simple struct:

pub struct MyStruct<T> {
    pub val: T
}

I'd like to implement the Add trait so I can write expressions such as S1 + S2, where S1 is an instance of MyStruct::<T1>, S2 is an instance of MyStruct::<T2>:

impl<T1: Add<T2>, T2> Add<MyStruct::<T2>> for MyStruct<T1> {
    type Output = MyStruct<<T1 as Add<T2>>::Output>;
  
    fn add(self, rhs: MyStruct<T2>) -> Self::Output {
        return Self::Output {
            val: self.val + rhs.val
        }
    }
}

There's one problem here: the Add trait is not implemented between dissimilar types (e.g. f64 and i32, or even f64 and f32). I'm unable to implement it myself (Error Code E0117). Because of this I can't add, e.g., a MyStruct::<f64> and a MyStruct::<i32>.

I'd like to design the generics such that the types are automatically promoted to a "higher type" (similar to the conversion / promotion rules in the Julia language). However, it's not clear to me how to do this using generics. Writing this by hand is straightforward using, e.g., i32::from(...) or a as f64, but this would defeat the purpose of using generics to avoid writing boilerplate by hand.

What would be the most clear and elegant way to define a set of promotion / conversion rules for the generic types in this context?

(Apologies in advance for any errors in the above post--I'm new to Rust and still wrapping my head around many of its concepts)

As far as I know, this is not possible. Type promotions are simply outlawed in Rust.

Using Add for numbers of different sizes / kinds has pretty unclear semantics. If you add a float and an int do you want a float or an int as the result?

If you have a clear answer for that in your use case, you could always define your own Add trait and implement it for the combinations you care about.

You wouldn't be able to use the + operator inside the trait implementations for MyStruct, but that would be a pretty minor annoyance.

You could define some Promote trait yourself (or find a crate that did it for you). Then rely on it for your wrapper.

But it will be a lot of boilerplate, and maybe defeats the point of the exercise. Perhaps macros are a better fit than generics in that case.

2 Likes

Thanks for these suggestions everyone. It sounds like macros may be the way to go, or simply writing it out manually depending on if macros can capture the logic I'm thinking of.