▲ | atq2119 3 days ago | |||||||||||||||||||||||||||||||||||||||||||
I'm not convinced by your example of hashing. Let's assume for the sake of argument that the standard library didn't implement Hash for i32. You could then have two crates, A and B, with different implementations of Hash for i32, and both could instantiate HashMap<i32>. This can be made to work if we recognize the HashMap<i32> in crate A as a different type than the HashMap<i32> in crate B. This only really works if orphan implementations are exported and imported explicitly to resolve the conflict that arises from a crate C that depends on A and B. If C wants to handle HashMap<i32>, it needs to decide whether to import the orphan implementation of Hash for i32 from crate A or B (or to define its own). Depending on the decision, values of type HashMap<i32> can move between these crates or not. Basically, the "proximity to code location" is made explicit in a way the programmer can control. This makes type checking more complex, so it's not clear whether the price is worth it, but it does allow orphan implementations without creating coherence problems. | ||||||||||||||||||||||||||||||||||||||||||||
▲ | withoutboats3 3 days ago | parent [-] | |||||||||||||||||||||||||||||||||||||||||||
Implementations are not imported at all because they are not named. Like I wrote, named implementations (ala ML modules) is a valid alternative, but one with a much greater annotation burden. You could imagine having named impls that are allowed to be incoherent as an additional feature on top of coherent unnamed impls, but to use them you would need to make any code that depends on their behavior parameterized by the impl as well as the types. In fact, you can pretty trivially emulate that behavior in Rust today by adding a dummy type parameter to your type and traits. Again, it's all a set of trade offs. | ||||||||||||||||||||||||||||||||||||||||||||
|