Generic impl conflict when the generic impl doesn't apply


#1

I have the following code:

trait A<X> {}

trait B<X> {}

struct S;

impl<X> A<X> for S {}

impl<X, T: B<X>> A<X> for T {}

And when compiling I get the error: "conflicting implementations of trait A<_> for type S".
Why does the compiler use the impl for T : B when considering S? And why does doing X = i32 (removing X from the list and adding a type X = i32) solve it?


#2

I believe this could strictly be allowed when the struct and both traits are from the same module, this is not an exception that is currently made.

What you are running into is that having both of these implementations added would make it a backwards incompatible change to implement B<X> for S. This could definitely be allowed when S, A, and B are all defined in the same module, but if they were defined in separate modules, it would turn an operation (adding a trait implementation to a struct) that is assumed backwards-compatible into a backwards-incompatible operation.

This could either be addressed with specialization, or with the Rust compiler being fixed/modified to accept this specific case to allow a blanket implementation + one specific implementation where both the blanket trait, the specific trait and the struct are defined in the same module.


#3

The reason is that a different trait would be allowed to write an impl like so:

struct Foo;

impl B<Foo> for S {}

Which would make those two impls overlap. The coherence checker does not take privacy into account when determining this. Setting the default type and removing it from the trait impl is equivalent to saying

impl<T: B<i32>> A<i32> for T {}

which does not overlap, because S does not implement B<i32>, and no other crate could add such an impl.


#4

According to rustc 1.16.0:

error[E0117]: only traits defined in the current crate can be implemented for arbitrary types

as well as:

error[E0210]: [...] only traits defined in the current crate can be implemented for a type parameter

So I don’t think you’d be able to impl B<Foo> for S in an external crate.


#5

only traits defined in the current crate

B<Foo> is considered a “local trait” if Foo is local

only traits defined in the current crate can be implemented for a type parameter

We’re not implementing it on a type parameter, we’re implementing it on your concrete type S.


#7

ok, so if I add:

trait C {}
trait B<X> : C {}

now any T : B must also impl C and therefore you can’t have impl B for S from an outside crate, right?