Thoughts on `pub impl`?


#1

Over at Revisit Orphan Rules, the following was said:

Addendum: pub impl

If I was desigining this from scratch, I would say that the syntax which aligns best with the language would be to require implementations to be pub if an external module wanted to use them. So, for example, if you just had one crate A and wrote impl T for S then within crate A it would be able to use trait T, but if some other crate B imported S from A the implementation would not also be imported. If, on the other hand, crate A had pub impl T for S then B could import trait T with some syntax like use impl T for S from A. This seems to fit best with the rest of the language’s pub features, but it would be a breaking change and makes types more complicated. I’d rather not let this pub aspect hold up the solutions discussed above.

Maybe I missed it but I didn’t see this discussed in that thread and thought it might be worth it on its own.

My motivation for pub impl (in a future edition?) is that sometimes, it is convenient to provide a trait implementation for your own crate but you don’t want it to be a part of your public API. Examples:

  • From implementations for your Error type. If you migrate from one crate to another for your implementation (like glob to globwalk), you’d end up dropping a From and adding a different From implementation. These From implementations only exist to make it easier for your to use ?.
  • In some cases, I’ve wanted to use Serialize on a type that I don’t want to support serde on for the sake of a cheap but good looking Debug or Display

#2

I think it is a fantastic idea to allow “hidden implementations” that still preserve coherence, because they must.

To that effect, I wrote a draft RFC some days ago:

It has already received some reviews I haven’t had time to integrate / fix yet. I would love more feedback and your input in finalizing the RFC before it is published for formal review.


#3

Just to clarify, is this proposal intended to address coherence at all? I initially thought so from the context of coming from the “Revisiting Orphan Rules” thread, but I don’t see how that would work (if one can e.g. instantiate HashMap<K, V> based on a private impl Hash for K, the “hash table problem” still applies if coherence is weakened) and now I noticed there’s no actual mention of that in this thread.


#4

As I see it, this change has little to do with coherence; the motivations listed in the OP are valid reasons for wanting “private impls” even if the existing coherence rules are upheld. Likewise, @Centril’s similar proposal explicitly states that the compiler will still treat all impls as public w.r.t. overlap.

Perhaps blanket private impls (those which can cause conflicts) should be discouraged, or displayed in rustdoc.


#5

If I’m reading the draft RFC right, coherence is a non-issue because it states that hidden impls are only allowed in the crate that defined the type. So this isn’t a weakening of the orphan rules at all.

In which case, my question is one of motivation. My understanding is that any time you want a “private impl” on a public type, you can already achieve that today with a private newtype. Obviously that involves some boilerplate, but imo a feature like this needs stronger motivation than avoiding boilerplate (the trait system is confusing enough as it is). Are there any compelling, concrete examples where a private newtype would not work at all?


#6

I can’t speak to coherence; I just care about easing API compatibility. My intention was to add my motivation to the motivation I quoted and I expect there are others.