2

I want to define a trait A. I can fulfill A on anything which is a std::default::Default but I want a special implementation for u64. The specialization feature is designed to do this, and the following code compiles fine:

#![feature(specialization)]

trait A {}
impl<T: std::default::Default> A for T {}
impl A for u64 {}

fn main() {}

Now I want to use a trait from another crate (protobuf::Message) instead of std::default::Default. The following code doesn't work:

#![feature(specialization)]
extern crate protobuf;

trait A {}
impl<T: protobuf::Message> A for T {}
impl A for u64 {}

fn main() {}

The compiler complains of conflicting implementations:

error[E0119]: conflicting implementations of trait `A` for type `u64`:
 --> src/main.rs:6:1
  |
5 | impl<T: protobuf::Message> A for T {}
  | ------------------------------------- first implementation here
6 | impl A for u64 {}
  | ^^^^^^^^^^^^^^^^^ conflicting implementation for `u64`

protobuf::Message is not implemented for u64, so there's no conflict, but that doesn't really seem relevant, because std::default::Default is defined for u64, and the specialization works fine for that case.

Since I specified the implementation for a concrete type, I would expect that to override implementations via traits.

Follow-up: I believe this is not a duplicate of Why do I get a conflicting implementations error when specializing a trait?, because this code works for traits defined in the standard library and fails for traits defined in external crates. In the other question, the mistake was in insufficiently strict trait bound specification. Here, the specializing trait is a primitive type, so that solution cannot apply.

Colin Merkel
  • 21
  • 1
  • 4
  • It might have to do with this: https://users.rust-lang.org/t/how-to-use-specialization-features/6023 – E_net4 Aug 26 '17 at 17:49
  • *so there's no conflict* — there's no conflict **now**, but that's not how these rules work. Your code should not stop compiling if the standard library or protobuf decide to implement the trait for the type. See https://stackoverflow.com/q/39159226/155423; https://stackoverflow.com/q/37347311/155423; https://stackoverflow.com/q/30845737/155423, etc. – Shepmaster Aug 26 '17 at 17:51
  • In that thread, somebody says "Specialization is currently only used in the standard library...". Does that mean you can only specialize over traits from the standard library? – Colin Merkel Aug 26 '17 at 17:51
  • Highly relevant recent question: [Why do I get a conflicting implementations error when specializing a trait?](https://stackoverflow.com/q/45873736/155423). – Shepmaster Aug 26 '17 at 17:52
  • Right, but whether or not there will eventually be a conflict is irrelevant, since `std::default::Default` is implemented for `u64`, so there's a direct conflict there. Yet the specialization works for that case. – Colin Merkel Aug 26 '17 at 17:53
  • *since `std::default::Default` is implemented for `u64`* — that's why it works. Your second case is not a **specialization** of an existing implementation, it's a completely orthogonal implementation, one that crosses the crate boundary and thus the orphan and/or coherence rules come into play. The specialization feature doesn't address those cases at all. – Shepmaster Aug 26 '17 at 17:59
  • 1
    I see: so the compiler is getting confused by the fact that the trait *isn't* implemented in `protobuf::Message`, and fails because it might conflict in the future - not realizing that it if it were, it would be fine, since specialization would take over. That sounds like a reasonable answer. – Colin Merkel Aug 26 '17 at 18:06
  • It's nothing to do with a "primitive type" or with "external crates". For example, your second code works with `protobuf::CachedSize` instead of `u64` because it's a type that protobuf implements `Message` for. *the compiler is getting confused* — no, the orphan / coherence rules are **deliberate** design decisions, there's no confusion or accident or bug. You are correct that the point is that if you could write the code you attempted to, a future update of the `std` crate or `protobuf` crate would cause your code to choose a different path, and that's why it's disallowed. – Shepmaster Aug 26 '17 at 18:14

0 Answers0