-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
pub(restricted) item #1422
pub(restricted) item #1422
Conversation
update: zebra stripe the embedded laundry list of bugs. update: try to clarify what I am talking about in the "In practice" section. update: add example of re-export. update: added pub(crate) example. update: added discussion of precedent in Scala.
|
||
(You may be wondering: "Could we move that `impl S` out to the | ||
top-level, out of `mod a`?" Well ... see discussion in the | ||
[unresolved questions][def-outside-restriction].) |
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 don't find this to be a convincing example. You could just leave out the pub(a)
and the example would work right now just as expected. Adding impls inside submodules is imo the correct way to say it's only accessible in there. The example should probably be showing two submodules accessing the same function. But then it doesn't fit here, but to to the unresolved question point.
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.
You're right, this was not a good example. I will try to fix.
I currently emulate this with traits defined at the level that needs visibility, but it's a lot of annoying boilerplate: https://github.com/sfackler/rust-postgres/blob/master/src/lib.rs#L1437-L1487 |
|
||
The main problem with this approach is that we tried it, and it | ||
did not work well: The implementation was buggy, and the user-visible | ||
error messages were hard to understand. |
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 reachability analysis is already performed by the privacy pass. Its results are used by rustdoc, dead code lint, stability and link-time-reachability passes, but not by privacy itself.
It's also less buggy than it was.
I like the idea, but I'm not sure about the syntax, you propose. |
I like the general direction, although I mostly have concerns about the implementation side, the current privacy system is not well formalized and three its main components - privacy checker itself, private-in-public checker and reachability analysis often work based on different assumptions and contradict to each other. Together with this and the new macro privacy system from @nrc it may bring some chaos. Also, fixing rust-lang/rust#30079 or relaxing the rules to allow
will probably involve implementing some rudimentary form of |
It would be really nice to try making |
tree." | ||
|
||
The current `crate` is one such tree, and would be expressed via: | ||
`pub(crate) item`. Other trees can be denoted via a path employed in a |
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.
We could have pub(::)
instead of pub(crate)
, this would mean only having a single syntax, at the expense of grotesque ugliness.
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.
my eyes!
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.
alternatively crate
could be allowed at the beginning of paths, thus making this a single syntax
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 then people will complain about having more than one way to do things (in this case, writing absolute paths) right?
Big 👍 on something in this direction. I've repeatedly felt contorted trying to make items public to the rest of my crate without exposing them outside the crate. Maybe I haven't worked on large enough libraries, but I am not sure that anything more powerful than
👍 on this if Rust ever increments the major version number. |
subtrees, narrow the feature to just `pub(crate) item`, so that one | ||
chooses either "module private" (by adding no modifier), or | ||
"universally visible" (by adding `pub`), or "visible to just the | ||
current crate" (by adding `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.
I'm strongly against this alternative - I think it is ill-advised to make crates more significant than they already are - we would end up with two layers of modularisation. I prefer to keep crates as essentially file-system artefacts.
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.
Obviously I agree with @nrc; otherwise this RFC would have been written to just suggest pub(crate)
on its own.
Of course we can see there are people on the other side of this debate; I've seen two commenters already state that they think pub(crate)
may suffice on its own.
@nrc I am curious if you can come up with other use-cases for pub(path)
beyond the hypothetical crate-merging refactoring tool that I sketched in the text of this subsection.
- Well, of course there is the obvious "some crates are large enough to warrant privacy controls within the crate, that are more fine-grained than just
pub
/pub(crate)
/private
I actually rather likes the proposal from @petrochenkov that we might roll this out in stages, with pub(crate)
very early on, and adding pub(path)
later after convincing the community at large that it is a good thing too.
Except I might advocate putting in pub(crate)
before we tighten up the rules for privacy at large, for the precise reason described in the RFC: giving people the ability to express pub(crate)
will encourage them to fix their code to say something closer to what they mean now and stop exploiting bugs in the privacy system to express such structures, which will give us more freedom to tighten up privacy (and tell them to use pub(crate)
in the warnings emitted during warning cycle period).
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.
before we tighten up the rules for privacy at large
I don't expect noticeable breakage from privacy in the future, most practically significant holes are closed now. Regarding the existing private_in_public
warnings, I don't think pub(crate)
will help much, but e.g. relaxing rules on trait bounds or alias substitution certainly will.
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 think it is ill-advised to make crates more significant than they already are - we would end up with two layers of modularisation.
I agree that this is not ideal, but how is this not already the case? Coherence rules operate at the crate rather than module level, extern crate
declarations and cargo dependencies are in themselves not transparent, etc. There is already a pretty big difference in the relation between modules inside my crate and relations with modules outside of it.
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.
With regards to privacy, crates are also important, as units of compilation - some code or metadata can potentially be omitted from the compiled crate if it can be proven that it is not accessible from other crates.
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.
@nrc I think the battle against crates as a separate unit of modularization has long been lost. The crate vs. mod distinction already matters for circular dependencies, inlining, and incremental recompilation. I don't see any danger to embracing this distinction even further.
@pnkfelix I have indeed seen people asking for "crate-level visibility" for years now (I'd be surprised if there isn't an old RFC for it somewhere), so paring down this proposal to merely pub(crate)
may very well be viable. And in that case you can even consider simplifying the syntax to just pub crate
or crate pub
or something.
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.
paring down this proposal to merely
pub(crate)
may very well be viable. And in that case you can even consider simplifying the syntax to justpub crate
orcrate pub
or something.
I am pretty strongly opposed to adopting a syntax that isn't future-extensible to pub(path)
... pub in crate <item>
(which then supports e.g. pub in path <item>
) might work, though visually I think I prefer the parentheses.
The typeck point-of-view is that associated items are as private as their containing traits and that all types are basically public. I am not sure about the motivation for the no-privates-in-public rule (even reading the comments does not make that clear) - we should clarify what properties we want from privacy. |
From my typeck POV, the NPIP/RFC136 rule feels to me rather lintish - it is not sure exactly what it is trying to prevent. Local reasoning on reachability is always kind of odd - you need to be sure nobody exposes your API in some indirect way - but having some block on direct uses would be fine. |
Yes I've read it. |
It seems somewhat unfortunate that What happens if the restriction is to a subpath? e.g. is the call to pub(module) fn foo() {}
fn bar() {
foo() // is foo visible here?
}
mod module {} |
Hmm, you're right; if I'm going to allow
See the unresolved question section on this topic. |
In addition to user-oriented properties, I'd like to have the next guarantee: Update: Ideally this should be done based on reachability and not |
There is no example with struct impls, but I assume they are covered as well? I've hit this in my bindings a few times, I wanted to do:
Now I'm instead doing this workaround:
Edit: Changed |
? |
@petrochenkov - that doesn't work because
...could work, but it leaks the internal layout of |
Huge 👍 I'm very glad and grateful you came up with this @pnkfelix . I've run into situations where this would be very useful and much more straightforward than the contortions that are required today, but I had this (what I hope to be mis)conception that changes to item visibility rules/functionality would be very difficult to convince the team about. Glad it's happening from the inside! 😁 |
fn semisecret(x: i32) -> i32 { use self::b::c::J; x + J } | ||
|
||
pub fn foo(y: i32) -> i32 { semisecret(I) + y } | ||
pub fn bar(z: i32) -> i32 { semisecret(I) * z } |
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.
Comment at the top says I
and foo
are exported. Not sure why bar
is also exported.
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 added fn bar
to the example later (just so "many places" that semisecret
would be called did actually mean "more than one place"), but forgot to update the comment above the mod a
.
to describe a crate-scoped-item:
|
I was giving something along the lines of |
Did we ever clarify this in particular? Longer term, I would expect glob re-exports to re-export all things that are visible to the "re-exporter" (with the same privacy). However, our current rules about shadowing make it a problematic breaking change to add these semantics, because
But I should go make sure I'm caught up on this thread... |
What happens if we want to expose a name to multiple modules? |
|
||
If a re-export occurs within a non-`pub` module, can we treat it as | ||
implicitly satisfying a restriction to `super` imposed by the item it | ||
is re-exporting? |
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 prefer to start with the strict rules (i.e. the answer "No"). They fit better into the whole picture and can always be relaxed if necessary (I doubt it will be necessary).
Huzzah! The @rust-lang/lang team has decided to accept this RFC. With respect to unresolved questions -- such as the strictness of certain rules -- I think we tend towards the stricter interpretation, but also intend to answer such questions after gaining some experience with an implementation. |
Tracking issue: rust-lang/rust#32409 |
@pnkfelix I think you still had some minor clarifications that you planned to make? |
Welp. This has been sitting in my notifications for so long that I never noticed the cc. Anyway:
|
@nikomatsakis I'll try to update doc today |
An alternative idea for the syntax:
This syntax might be more intuitively associable with paths than the proposed syntax? |
I know I'm late to the party, but I just read this RFC and noticed the section where the author isn't sure about precedents to the suggested solution. The visibility system in Bazel (the build system) is basically identical to the this |
nomenclature idea: lets make "pub" be short for publish, if you don't specify where you publish it to everywhere, when you restrict it then you just publish your function/struct/member to them. |
Summary
Expand the current
pub
/non-pub
categorization of items with the ability to say "make this item visible solely to a (named) module tree."The current
crate
is one such tree, and would be expressed via:pub(crate) item
. Other trees can be denoted via a path employed in ause
statement, e.g.pub(a::b) item
, orpub(super) item
.(accepted) RFC text
rendered draft