How Can I Compute the Union of Two Generic Traits?

I’m trying to define a trait implementation where a tuple (L, R) implements Handler<T> if either L or R implements Handler<T>. I’ve been able to define the intersection case, where both L and R implement Handler<T>, like this:

trait Handler<T> {
    fn handle(&mut self, item: &T);
}

impl<T, L, R> Handler<T> for (L, R)
where
    L: Handler<T>,
    R: Handler<T>,
{
    fn handle(&mut self, item: &T) {
        let (l, r) = self;
        l.handle(item);
        r.handle(item);
    }
}

However, I can’t figure out how to express the union case—where (L, R) implements Handler<T> if either L or R does. Ideally, I’d like to write something like:

impl<T, L, R> Handler<T> for (L, R)
where
    L: Handler<T>,
    R: DOES_NOT_IMPLEMENT Handler<T>,
{
    fn handle(&mut self, item: &T) {
        self.0.handle(item);
    }
}

impl<T, L, R> Handler<T> for (L, R)
where
    L: DOES_NOT_IMPLEMENT Handler<T>,
    R: Handler<T>,
{
    fn handle(&mut self, item: &T) {
        self.1.handle(item);
    }
}

But Rust doesn’t allow expressing “does not implement a trait” as a bound.

This limitation is problematic for statically-typed architecture designs. For example, say I have two actors handling different message types (i32 and String). I’d like to combine their addresses into a single Channel that can send both types. Without a way to express the union constraint, I’m forced to:

  1. Use runtime polymorphism (dyn Any + TypeId + HashMap).
  2. Manually handle each concrete type, giving up polymorphism.
  3. Discover an alternative technique I haven’t thought of yet!

Is there a more idiomatic or elegant way to achieve this in Rust?

1 Like

Trait specialization is currently unstable.

pub enum Message {
    Text(String),
    Number(i32),
}

impl Message {
    ...
}
1 Like

Thanks for the reply! Yeah, it’s unfortunate that trait specialization is still unstable. Looks like using an enum and giving up polymorphism is the only way to go for now. Appreciate your input!