You're running into something called Coherence. That is, the compiler needs a way to always determine whether a type implements a particular trait and if so, which impl block should it use.
As has already been mentioned, the problem with allowing both Blarg
impls is that if some type Foo
implements both BlargA
and BlargB
it's not possible to decide which impl to use for <T as Blarg>::blarg()
.
Your proposition of just making it a compile error to implement BlargA
and BlargB
at the same time, while seemingly fine at first glance, has a couple problems... The most severe being that it moves the compilation error away from the code which is at fault (the overlapping impl<T: BlargXXX> Blarg for T
blocks) and over to where it is used.
This is how C++ templates work and leads to hard-to-debug compile errors and non-local reasoning.
Depending on your situation, you've got a variety of tools at your disposal.
- Specialisation - if the compiler can see that one implementation is strictly more specific than another, the decision is no longer ambiguous
- A newtype wrapper which implements
Blarg
via BlargA
, and another wrapper which does the same for BlargB
- Use helper functions instead of a helper trait
- Add another layer of indirection (see things like the
futures::future::IntoFuture
and IntoIterator
traits)
- Rephrasing
Blarg
(or the specialised BlargA
and BlargB
) so you don't get into this situation (probably not a helpful response, but worth mentioning anyway)