So I think sealed traits probably make sense, but perhaps not quite for the reasons you suggest in this motivation. Some reasons that I have wanted sealed traits are as follows:
- I want to define a closed set of types that is not open to user extension.
- For example, imagine that I am dealing in unsafe code, and I want to write a function that accepts either A, B, or C. I could write
fn foo<T>(x: T) where T: MySealedTraitand just implementMySealedTraitforA,B, andC, but of course then the user can implement it for their own types. IfMySealedTraitis just a marker trait, this may not make sense. - That said, we have options today:
- declare the trait as unsafe, for example
- use some privacy trick to make the trait unnameable by users
- Still, I feel like this comes up as something I want semi-regularly. Whether it's something I need feels a little less clear.
- (Reading a bit further, this is basically what @petrochenkov was talking about.)
- For example, imagine that I am dealing in unsafe code, and I want to write a function that accepts either A, B, or C. I could write
- Publish a trait whose design is still being changed without fear that users will try to implement it.
- If a trait is sealed, you can make arbitrary changes to its members without violating semver, so long as you keep (or expand) the same set of impls.
- But maybe it's enough to just comment the item as unstable and exempt from semver rules? Unclear -- it makes us very nervous for the lang itself, I suspect popular crates would have the same problems of de facto stability.
One open question: should sealed affect how auto traits like Send apply to trait objects? In principle, we can now find all implementing types, after all. I can see arguments on both sides: for example, that makes removing Sealed potentially a breaking change! (But only if other crates are relying on this property when using a trait object.)
I think this is not quite correct. I think the concern is that somebody could implement the trait FromSql, not FromSqlRow (this may just have been a typo on your part). If they did so, they would create an indirect dependency. This seems like Coherence and blanket impls interact in a suboptimal fashion with generic impls · Issue #19032 · rust-lang/rust · GitHub -- which is definitely a frustrating problem.
Therefore, I think @withoutboats is correct that explicit negative impls would help here. @aturon and I have also been discussing the possibility that specialization could indirectly come to the rescue: that is, it might be that we could allow some overlap, on the basis that if the user did add an impl, it wouldn't matter, because the impl would have lower precedence than the existing one. So, in some user did add a FromSql impl, then yes, that would trigger overlap, but it's still the case that your second impl is more specific than the first one, and hence it would still be considered the winner.
To be honest I haven't made up my mind on this issue and am still mulling it over. It seems to me that having the ability to make explicit negative declarations can be very clear and explicit. However, it's also another level of complexity in an already complex system. (Interestingly, sealed is an example of a negative impl that you would not be able to say in any explicit proposal I've seen.)
Ah yes, this is basically what I was talking about above.
This seems like a bit of a distinct issue. In particular, I had assumed that sealed controlled where a trait could be implemented, but not where its members could be accessed. To control access, it seems like the obvious solution is to permit trait items to have public/private declarations. I've been thinking that https://github.com/rust-lang/rfcs/pull/1422 provides a potential solution there: we could allow (optional) pub declarations on trait items, which would indicate where they can be used (but not where they can be implemented). So, for example, pub(crate) could be used to restrict a trait item to the current crate, and would be a natural complement to sealed.
Well, it's always possible to declare a public trait -- and thus one hypothetically exposed to the world -- but "seal it off" through an inaccessible path http://is.gd/yqIPc5, no? (I didn't look at your example in great detail, perhaps there is some other wrinkle I'm missing)