I hope it’s ok to resurrect this old thread. I’ve been reading Niko’s blog post and this and some other discussions and I’m hoping that maybe someone could comment on the following questions:
When you define an orphan impl of a trait, is it actually reasonable to depend on this particular implementation in the rest of your code? Assuming that the trait properly documents all expected invariants, pre- and post-conditions, shouldn’t it normally be enough to know that there exist implementations for the types you care about and that the whole program uses a unique trait implementation for any particular combination of type arguments?
If this is the case, wouldn’t it be enough to have rules for selecting a unique impl when the final executable is compiled and linked together? All trait impls would be automatically exported into a global registry, i.e. they wouldn’t have a separate visibility, and the compiler/linker would be responsible for selecting a unique impl for any particular type parameter combination of the trait. At the crate level one could allow or even require developers to specify rules for the resolution and disambiguation of orphan impls.
Has this approach been considered? One drawback of this approach is that it would reduce the scope for separate compilation or reduce optimization opportunities in separately compiled code. Another drawback is that it would make the applied disambiguation rules, which may not be very visible, part of the effective semantic version of a binary. The severity of both issues depends a lot on the exact implementation.
Update: When I originally wrote this reply, I didn’t fully appreciate the problems this approach would create for separate compilation. If one allows orphan impls and requires a globally unique resolution of impls, the compiler can not be sure that a trait is not implemented for some type unless it has seen the whole program. So apart from the issue that an orphan trait impl may be replaced during compilation to enforce coherence, there is also the issue that any kind of overload or specialization resolution that hinges on the absence of a trait implementation for a type may be changed if a crate is linked together with a crate that has an orphan impl.