6

Say I have this code:

pub trait A {}
pub trait B {}

pub trait SomeBehavior {
  fn func() -> bool;
}

And I want to provide the blanket implementation for A and B like this:

impl <T> SomeBehavior for T where T: A {
  fn func() -> bool { true }
}

impl <T> SomeBehavior for T where T: B {
  fn func() -> bool { false }
}

But this gives following error:

error[E0119]: conflicting implementations of trait `SomeBehavior`
  --> src/lib.rs:12:1
   |
8  | impl <T> SomeBehavior for T where T: A {
   | -------------------------------------- first implementation here
...
12 | impl <T> SomeBehavior for T where T: B {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation

Why the compiler treats two different implementations on different traits as same implementation?

kmdreko
  • 42,554
  • 6
  • 57
  • 106
Jaebum
  • 1,397
  • 1
  • 13
  • 33
  • What with a type that implements both `A` and `B`, what implementation will it pick? – Chayim Friedman Sep 20 '22 at 06:55
  • _Why the compiler treats two different implementations on different traits as same implementation_ What if `T` implements `A` and `B` at the same time ? – Ömer Erden Sep 20 '22 at 06:55
  • Well if such type exists the compiler should return an error. But why would the compiler hastily expects that? – Jaebum Sep 20 '22 at 07:06
  • 2
    In the general case, it's undecidable for the compiler whether such a type exists, because with genercis there's an infinite number of types, and the implementations of `A` and `B` may depend on complex trait bounds again. The "non-overlapping impls" rule will eventually be relaxed when a feature called "specialization" lands, but my understanding is that your code will be invalid even then. If you want to know more about this topic, search for "rust coherence" in your search engine of choice. – Sven Marnach Sep 20 '22 at 07:45
  • *"why would the compiler hastily expects that?"* - Rust has taken the stance that generics should be devoid of issues in *principle*, not just in *practice*. So if the compiler sees that two implementations *could* conflict, it will reject that code. – kmdreko Sep 20 '22 at 16:00
  • I see. Thanks for the comment.s If you just put your comment into an answer, I will accept it. – Jaebum Sep 20 '22 at 16:01

1 Answers1

4

Rust has the concept of "trait coherence" which is the idea that for any combination of type and trait there should be at most one implementation of that trait. This is the concept that guides Rust's "orphan rule" and is designed to ensure that conflicting implementations can't occur in downstream crates.

So the problem with the two blanket implementations above is the possibility that a type can implement both A and B. There would then be two possible implementations for SomeBehavior for that type.

struct MyType;
impl A for MyType;
impl B for MyType;

let t = MyType;
let _ = t.func(); // how to choose?

Its not particularly well documented part of Rust unfortunately; likely because its complicated, its contentious, the rules have changed over the years, and will likely change again in the future. Resources are scattered between blog posts, RFCs, and issues but you can find the important bits in the Coherence section of the chalk book (the next-generation trait-solver).

There may be features added in the future that may allow this or something similar:

kmdreko
  • 42,554
  • 6
  • 57
  • 106
  • This is a great answer, but how do I work around this? I can a trait have multiple blanket implementations? – Shahar 'Dawn' Or Mar 06 '23 at 05:18
  • 1
    No, a trait cannot have multiple blanket implementations [even when they don't overlap](https://github.com/rust-lang/rust/issues/20400). Workarounds depend on the use case and could be done via wrapper types, [an extra layer of abstraction](/a/40408431/2189130), or avoid the problem entirely by using separate functions/traits instead of handling everything under one interface. – kmdreko Mar 06 '23 at 18:15