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:
- Use runtime polymorphism (
dyn Any + TypeId + HashMap
). - Manually handle each concrete type, giving up polymorphism.
- Discover an alternative technique I haven’t thought of yet!
Is there a more idiomatic or elegant way to achieve this in Rust?