▲ | withoutboats3 3 days ago | |||||||||||||||||||||||||||||||||||||||||||||||||
Implementations are not exported or public at all: they are used in functions and those functions are exported. For correctness, you want those implementations to be resolved consistently (this is what coherence is). This post gives the example of unioning two sets: you need to know that they're ordered the same way for your algorithm to work. So the problem isn't that the implementation is public, it's that its used somewhere by a function which is public (or called, transitively, by a public function). For a library, code which is not being used by a public function is dead code, so any impl that is actually used is inherently public. You might say, okay, well can binaries define orphan impls? The problem here is that we like backward compatibility: when a new impl is added to your dependency, possibly in a point release, it could conflict with your orphan and break you. You could allow users, probably with some ceremony, to opt into orphan impls in binaries, with the caveat that they are accepting that updating any of their dependencies could cause a compilation failure. But that's it: if you allow this in libraries, downstream users could start seeing unsolvable, unpredictable compilation failures as point releases of their dependencies introduce conflicts with orphan impls in other dependencies. | ||||||||||||||||||||||||||||||||||||||||||||||||||
▲ | lesuorac 3 days ago | parent | next [-] | |||||||||||||||||||||||||||||||||||||||||||||||||
It would still be consistent; everything with my crate resolves `impl Foo for Bar` to what I define, everything with other crate resolves `impl Foo for Bar` to what they defined, and any other crate would have a compilation error because those crates didn't `impl Foo for Bar`. If I for some reason exported a method like `fn call_bar(foo: Foo) -> Bar` then I think it would use my `impl Foo for Bar` since the source code for the trait impl was within my crate. What happens if instead I export like `fn call_bar<F: Bar>(foo: F) -> Bar)` is probably a bit more up to debate as to whose trait impl should be used; probably whichever crate where F being Foo is originally known. I think they did say binaries can define ophan impls; and the only way somebody should be able to break your code is by changing the trait definition or deleting the implementing type. Otherwise your implementation would override the changed implementation. This seems fine because even if I locally define `Foo` which lets me to `Foo impl Bar`; if you then delete Bar then my code breaks anyways. | ||||||||||||||||||||||||||||||||||||||||||||||||||
▲ | pornel 3 days ago | parent | prev [-] | |||||||||||||||||||||||||||||||||||||||||||||||||
How about downgrading duplicate implementation in the binary to a warning? SQL has CREATE TABLE IF NOT EXISTS. Rust could have `impl Trait if not already implemented`. | ||||||||||||||||||||||||||||||||||||||||||||||||||
|