Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do blanket implementations for two different traits conflict?

Tags:

rust

traits

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?

like image 850
Jaebum Avatar asked Jan 26 '26 16:01

Jaebum


2 Answers

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:

  • negative impls (unstable feature)
  • specialization (RFC)
  • crate-level where-clauses (blog article by Niko Matsakis)
like image 92
kmdreko Avatar answered Jan 29 '26 12:01

kmdreko


Hacky workaround:

pub trait A {}
pub trait B {}

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

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

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

impl A for i32 {}
impl B for i32 {}

fn do_sth_with_a<T: A + std::fmt::Display>(t: T) {
    println!("do_sth_with_a({}): T::func(): {}", t, T::func());
}

fn do_sth_with_b<T: B + std::fmt::Display>(t: T) {
    println!("do_sth_with_b({}): T::func(): {}", t, T::func());
}

fn main() {
  do_sth_with_a(32);
  do_sth_with_b(32);
}

Output:

do_sth_with_a(32): T::func(): true
do_sth_with_b(32): T::func(): false
like image 38
Andy Avatar answered Jan 29 '26 11:01

Andy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!