Perhaps you're already aware of trait objects? That's the exact match for your purpose to have U
dynamically change for the fixed T
.
However, two problems prevent the direct usage of Add
or Sub
as a trait object:
Add
and Sub
aren't object-safe, as it expects a by-value self
argument.
- Trait objects are existential over
Self
, but you want trait objects that are existential over RHS
of Add
/Sub
.
Therefore you'll have to do extra work to introduce intermediate traits:
use std::ops::Add;
trait MyAdd<T> {
fn my_add(&self, other: T) -> T;
}
impl<T, U> MyAdd<T> for U
where
T: Add<U, Output = T>,
U: Clone,
{
fn my_add(&self, other: T) -> T {
other + self.clone()
}
}
enum Foo<T> {
Bar(Box<MyAdd<T>>),
}
If you don't want U: Clone
, there are options depending on your usage:
- Assume
T: Add<&'a U, Output = T>
instead of T: Add<U, Output = T>
- Use
self: Box<Self>
instead of &self
in fn my_add
Edit: operator overloading
The proxy trait MyAdd<T>
above doesn't proxy operator overloading by itself; thus you'll have to manually implement std::ops::Add
for this.
// Coherence error
impl<T> Add<Box<MyAdd<T>>> for T {
type Output = T;
fn add(self, other: Box<MyAdd<T>>) -> Self::Output {
other.my_add(self)
}
}
However, this code doesn't work due to coherence restrictions. This restriction is fundamental because it's always possible that a sibling crate has an impl like impl<T> Add<T> for TypeDefinedInTheSiblingCrate
, which surely overlaps with the impl above.
One possible workaround is to use the newtype pattern:
struct Wrapper<T>(T);
impl<T> Add<Box<MyAdd<T>>> for Wrapper<T> {
type Output = Wrapper<T>;
fn add(self, other: Box<MyAdd<T>>) -> Self::Output {
Wrapper(other.my_add(self.0))
}
}
Then you can write things like Wrapper(x) + boxed_myadd_value + another_myadd_value
.
` ? This would require a new type, I'm looking for a way to do it with trait objects.– Flarp Jan 03 '18 at 01:05