How about this?
- Crate A that defines a Trait Foo
- Crate B that defines s Struct Bar
- Crate C that wants to implement A::Foo for B::Bar
- Crates D… that may also depend on any combination of A/B/C
The rules are:
- If A, B, and C are the same crate, no problem
- If B, C are the same, no problem
- If A, B are the same, no problem
- If A, B, and C are all different Crates:
- A providing implementation of A::Foo for B::Bar , no problem (though, if it is being added as a new impl, then it should be a major semver bump because a new pub api is being added that could break things)
- B implementing A::Foo for B::Bar, no problem (same issue WRT semver bump if being added after publishing)
- C implementing A::Foo for B::Bar, no problem as long as A and B both don’t currently implement it (if in the future, either of them implement it, then, it should be a breaking change as indicated by the semver bump of the dependent crates and create C will get a compiler error for redundant/conflicting trait impl)
So, that handles, A/B/C, but what about D…? Well, the only case that matters here is the “C” case where C provides an impl of A::Foo for B::Bar; otherwise, C is irrelevant.
OK, so in the case where C implements A::For for B::Bar we have the following possibilities:
- If one or more of D… depends on only A or B it’s not an issue
- If a D… depends on A AND B, but, not C AND is NOT being linked in a lib or bin with C then the rules apply as if it were C WRT A and B
- If a D… depends on A, B, and depends on C, then you have the following:
- if C implements A::Foo for B::Bar then D may not
- if C doesn’t implement A::Foo for B::Bar then D may implement it - if later, C, A, or B adds and implementation, then D fails to compile due to redundant impl
- If D… depends on A, B but not C, but is ultimately linked in a lib/bin
- If C provides an impl of A::Foo for B::Bar then the bin/lib will fail to link with a linker error saying that you have conflicting defs of the impl between D and C
- If C doesn’t provide the impl, everything works, if later C adds an impl (a semver major bump), then D and C will fail to link together in the same lib/bin again (everything compiles OK though)
The above rules, of course, don’t take into account the Generic Trait impls provided by the Trait definition Crate A which are allowed to be overridden by specific impl as according to these rules.
I believe these rules would not allow conflicting definitions in a dependency tree for a crate, neither would they allow conflicts in a linked lib/bin. If something changed in a non-dependent crate such that we have two crates that don’t depend on each other both trying to provide impls for the same 3rd party trait for the same 3rd party struct, then, linking (but not compiling) would fail but that would be OK because the newly added trait to one or the other crate would have had to have been a Major Semver bump (which indicates a breaking change to the API).
Do these rules cover all cases?
I think these rules can be simplified to:
- If a crate A depends on some set S of other crates and any crate Si in that dependent set provides and impl of a trait Ta defined in Sa for a struct Yb defined somewhere in Sb, then A may not provide Ta for Yb as well. If it attempts to, it is a compiler error. After the fact additions of Ta in Sa for Yb in Sb are a semver change to either Sa or Sb and so is a breaking change for A and is a compile error for A.
- If a crate A will be linked with crate B and both depend on set S of crates, then if both A and B attempt to provide Ta for Yb, then they will fail to link together in a bin/lib. If A or B didn’t originally provide Ta for Yb and then the one that wasn’t attempting to provide it is added, that is a semver major change (breaking change) and so OK to have a link failure where one wasn’t present previously.
If a crate doesn’t bump the major semver number when adding a impl that wasn’t previously there, things still work, but, they’ve violated the semver contract and consumers of their crate will be peeved that they’ve added a breaking change without bumping the semver as the rules require.