Mutually exclusive traits

In current Rust, there doesn’t seem to be a way to declare two traits as mutually exclusive.

For example, suppose I have two traits A and B that are semantically mutually exclusive. I also have a struct Example that takes a generic parameter T: C , where both A: C and B: C .

trait C {}
trait A: C {}
trait B: C {}
// A and B are semantically mutually exclusive.
struct Example<T: C>{ _marker: PhantomData<T> }

Writing impl<T: C> Example<T> {} is perfectly fine. Then writing impl<T: A> Example<T> { fn test() {} } also seems acceptable. However, when I try to also write impl<T: B> Example<T> { fn test() {} } , the compiler complains that the two implementations conflict.

impl<T: C> Example<T> {} // ok

impl<T: A> Example<T> {
  fn test() {}
} // still ok

impl<T: B> Example<T> {
  fn test() {}
}// currently not ok

In fact, the compiler is not wrong — there could conceivably be a weird type that implements both semantically exclusive traits, leaving the compiler uncertain how to proceed. But currently, there is no way to declare that two traits are mutually exclusive. I understand that there is currently an unstable feature called "negative trait impls," but that seems to be intended for auto traits. Perhaps there is a better way to achieve this?

Negative impls are particularly relevant to auto traits, but they are equally usable for all traits. I don’t know if there are any plans to make negative impls usable to enable otherwise conflicting impls.

Another way to express mutual exclusion is to define a single trait, and discriminate the two cases using an associated constant or associated type:

#![feature(min_generic_const_args)]

use std::marker::PhantomData;

trait C {}
trait AOrB: C {
    type const IS_B: bool;
}

struct Example<T: C>{ _marker: PhantomData<T> }
impl<T: AOrB<IS_B = false>> Example<T> {
  fn test() {}
}

impl<T: AOrB<IS_B = true>> Example<T> {
  fn test() {}
}

Conceptually, this should already work (if you use an associated type instead of a const), because a single impl of AOrB can’t have two values of IS_B, but the compiler doesn’t currently recognize this type of non-conflict.

Yes, negative bounds, but they're highly experimental and basically not proposed for stabilization.

AFAIK there's a general "yes, that would be nice" wish for that to eventually be the case, but no concrete plans.

For example, I would like negative impls to replace the concept of fundamental traits, so we could do things like impl !FnOnce for String to solve some of the pattern-related reasons that certain traits are fundamental today.

2 Likes