-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Draft] RFC: Hidden implementations #7
Conversation
text/0000-hidden-impls.md
Outdated
Allow a visibility modifier on a `trait` implementation such that: | ||
|
||
+ it is public wrt. coherence and overlap, | ||
+ it is not usable in contexts it which it is *hidden* in. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sentence seems to have gotten mangled.
text/0000-hidden-impls.md
Outdated
```rust | ||
// crate B: | ||
|
||
impl Property for Thing { .. } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused how this would be allowed anyway as (AIUI) neither Property
nor Thing
are defined in crate B, so this impl would be invalid, overlap or no overlap.
text/0000-hidden-impls.md
Outdated
|
||
## `priv` visibility | ||
|
||
To be able to make implementations module private, this RFC introduces a new |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't people who need this do pub(in self)
? Seems like that would be less surprising than "priv"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or priv
could be just made an alias of pub(self)
(similar to crate
being an alisa of pub(crate)
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apparently I was oblivious to the fact that pub(self)
is already stable - so I'll make this section "possible future work".
text/0000-hidden-impls.md
Outdated
implementation of `Property` for `Thing`. Crate A is also under no obligation | ||
under the semver rules to provide an erased `Thing` value. As long as the value | ||
returned behaves observably in an equivalent manner, for some semantic | ||
definition of "equivalent", no breakage has occurred. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So essentially, return Box::new(Thing): Box<dyn Property>
is permitted with no compiler warnings? I'm confused why the compiler cannot lint against this -- it knows that it is creating a vtable for the impl of Property for Thing; as such that place can lint against this (though in many cases only after/during monomorphization).
However, I'm not opposed to this (just feels like the wording could be changed).
|
||
+ If the *default visibility* for some *context* is specified in that context | ||
*directly* in a source file, then a warn-by-default lint `redundant_vis` | ||
is raised. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, so we hard-lint currently on pub in the the below impl; in a way, I could see this being a way to define the "extensible traits" talked about on Discord -- we could say that you could make certain methods of a trait not world-visible. I guess you can also do that with two (or more) traits, but this seems like a reasonable extension of this RFC.
trait Foo {
pub fn bar() {}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure; you could have something like:
// crate A
pub trait Foo {
crate fn bar();
pub fn baz();
}
and since only crate A can provide a definition for bar
, then only crate A can provide implementations for Foo
.
I'll leave this out for now tho :)
text/0000-hidden-impls.md
Outdated
6. Should `pub impl ..` be linted against suggesting `impl ..` instead? | ||
|
||
7. Should `priv field: Type` and the `priv` visibility modifier in general | ||
be linted against when it is the contextual default? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to say that the priv
vs pub(self)
etc debate is also something to solve after merging the RFC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely; Now that I know pub(self)
is stable, we should punt on priv
.
The following questions are in the scope of the RFC and should be resolved | ||
*before* the RFC is merged: | ||
|
||
1. Are there any strange interactions with possible plans for negative bounds? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not negative bounds, but you do need to consider effect of negative impls of auto-traits:
priv impl !Send for MyUnsafeType {}
I suggest that the visibility of a negative impl must be ≥ that of the trait, otherwise it would be an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... this one is tricky... The impl above should be well-behaved (and not unsound)? and be interpreted as un-implementing Send
for MyUnsafeType
inside the module, but not elsewhere. Thus, the fact that MyUnsafeType: Send
may be observed outside but not inside the module.
However; I can't see any use cases for this, and it will likely just lead to bad places; so your proposed rule sounds good, at least for now.
One of the biggest unresolved question is that, given rust-lang/rust#39935 is still not fixed (which forced every impl in libstd to be insta-stable), how would I know this RFC would be implementable 🤔 |
…teaching, motivation.
Addressed all review comments thus far and made other improvements. :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had time to read up through the start of the reference section; will read the rest later. Overall, this seems very solid and thoroughly thought out, and I think would fit nicely with some other proposals in the air (like being able to write inherent impls for nonlocal types as long as those impls are marked with sufficient privacy).
``` | ||
|
||
# Motivation | ||
[motivation]: #motivation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Solid, well-explained motivation!
|
||
## The effect of hiding | ||
|
||
### Error E0277 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that there's a non-trivial interaction with macros here; currently, "privacy hygiene" is not a thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you elaborate a bit here? The way I'm thinking is that if we have a macro with say:
macro_rules! mac {
() => {
fn return_property() -> impl Property {
make_a_thing() // ← error[E0277]
}
}
}
which is then called at some site, then the visibility context used is that of the call site of mac!()
.
The same should be the case with a macro defining an implementation; it too is visible according to the call-site and not the definition site of the macro.
eventually becoming public. Since the compiler is aware of `Property for Thing` | ||
and that we've brought `Property` into scope, a warning will be emitted | ||
in addition to the `unused import` lint, suggesting that you should use | ||
*uniform function call syntax (UFCS)* instead: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent.
problem as with named implementations, which would be decidedly unsound in Rust | ||
since [*"coherence is an invariant that unsafe code is allowed to assume"*][unsound-named]. | ||
|
||
## Hiding things from yourself |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
`pub(super)`) be permitted on `impl` to hide implementations from | ||
different parts of the crate. | ||
|
||
## `pub` by default |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is, to my mind, the biggest drawback of the proposal. I don't see a good way around it, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, I agree on this being a drawback :) I added a paragraph discussing it in the section on Drawbacks.
= note: #[warn(redundant_vis)] on by default | ||
``` | ||
|
||
## Private visibility, `pub(self)` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
||
then the lint `redundant_vis` will be raised. | ||
|
||
## `#[derive(crate Trait)]` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
is more visible than `crate`. It will do so by clamping a `pub` visibility | ||
to `crate`. This applies generally for any visibilities. | ||
|
||
## In relation to trait objects |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
API, be they internal APIs within a project, or the public APIs of a crate. | ||
In most cases, you implementations will not need to be hidden. | ||
|
||
## Teaching beginners about hidden implementations |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
but not wanting to commit to there being a default value. | ||
|
||
However, we have no way eat our cake and keep it. We can't segregate who | ||
has access to what implementations by visibility. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing to potentially add/explore perhaps is to take a look at the compiler's TyCtxt
and similar types that are used throughout, but can't currently be implemented directly on outside of librustc
; it feels loosely like that's another significant motivation -- currently there's lots of functions that should/could be inherent on tcx but can't be.
Time to publish. |
This is a draft version of an RFC for you to review, before a formal proposal is made for consideration.
The RFC introduces a concept I am calling "hidden implementations".
Note that this idea is radically different than modularization of implementations as permitted in Idris, Scala and such languages in that it preserves coherence keeps the language free of orphan implementations.
I have left a bunch of unresolved questions which I'd like special attention given to. If you, the reader, have some rationale, motivation, drawbacks that you feel are missed, please do include them in your review. I would also love it if you find better ways to explain concepts than in this draft.