-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Documenting and fixing resolve's notion of public/private #8215
Comments
I stated my preference in #3505 but will restate here. I would like there to be a rule: "pub items in a non-pub mod are visible to the rest of the crate, but not visible to users outside the crate". This is necessary to finally seal the leakiness of the select trait abstraction, cf #5160. AIUI condition 2 listed above would imply this rule. |
Ok, to give a concrete (if contrived example), lets say I have this: pub mod inner_api_a {
use super::inner_common_impl;
pub fn api1() {
inner_common_impl::common1()
}
pub fn api2() {
inner_common_impl::common2()
}
}
pub mod inner_api_a {
use super::inner_common_impl;
pub fn api1() {
inner_common_impl::common3()
}
pub fn api2() {
inner_common_impl::common4()
}
}
mod inner_common_impl {
fn common1() { }
fn common2() { }
fn common3() { }
fn common4() { }
} In this example, The above will not compile, as If I mark it as public (so now it is visible cross-crate) it still won't compile because none of the functions are marked public. The only solution there is marking the function as public, thus making it visible to the outside world. There are some convolutions you can do regarding using The thing is, internal to the crate, the visibility rules are fine, and that's what matters while compiling that crate. When it comes to users of the library, it only matters that they can't link against the symbol you want. This suggests an alternative mechanism for controlling "link-time" visibility might be in order. By keeping the current visibility rules regarding For comparison, in C, everything in a library is exported by default, unless the function/variable is marked static, in which case it doesn't even escape the object it's in. This is similar to rust's I'm proposing a Another potential advantage is one that C++ benefits from the same system. Not exporting mountains of monomorphised symbols. |
Sub-bug of #6143 |
Meta-bug is milestoned. Denominating this one. |
Nominating for the "priority" label and also to de-milestone the metabug while milestoning this one instead. |
I just closed a few issues that I believe will be wontfix or fixed as determined by the outcome of this issue. After reviewing the various use-cases, I'm not entirely sure that my previous set of rules is actually the best thing to follow. Especially with @bblum's and @Aatch's comments, I would actually be in favor of @bblum's suggestion, written down as:
I am coming to prefer these rules because not only are they simple to implement, but they are simpler to understand and also work with. If this set of rules is agreed upon, I will update the issue text at the top to avoid later confusion over what should get implemented. |
1+ for those rules.
|
Also +1 for those new rules. Much simpler than the initial ruleset, and makes it easy to express exactly what I want. |
I think there are some more edge cases here (especially from the view of generating documentation). Currently:
e.g. // other.rs
pub use self::bar::baz;
pub mod foo {
struct Foo { x: int }
pub fn mk_foo() -> Foo { Foo { x: 1 } }
}
mod bar { pub fn baz() { println("escaped!!") } } extern mod other;
fn main() {
// other::baz(); // fails with a link-time error, *not* a compile time one
println!("{}", other::foo::mk_foo().x);
} I imagine that tightening these would be required too. |
Hm, in case of documentation I see basically two cases:
Of course, the question is if we should allow returning not-externally-public types from externally-public functions at all. If yes, then I claim the same rules should apply as if it where Also note that you might get the type of an return value with an hypothetical |
Accepted for well-defined |
@alexcrichton I like the rules, but it's worth noting that these proposed rules means a I seem to recall an earlier proposal was that a module had access to all of its direct parent's priv items as well. This would solve these concerns, though I don't know if it's worth the complexity. |
As an update on this, I was talking with @nikomatsakis and he's enlightened me as to what the intended privacy rules actually should be. The idea behind the intended rules were:
The first rule is fairly obvious, but the second rule is a little bit more subtle. What this means is that you can use any number of "helper modules" in your ancestry. It doesn't mean that you can use anything in a helper module, it just means that you can look into your ancestor's private modules. At that point anything you use must be This encompasses the I've got a branch-in-progress to refine the current system. My idea was to completely rip out privacy from resolve and put it all in the privacy passes. This should yield better error messages and be more maintainable in the future. I'm mostly doing this as a guinea-pig type situation to make sure that this works out. I've come to like the second rule, though, as it makes sense to me and it seems to encompass almost all use cases. |
This might fall under item 2. already, but I just noticed that if you do something like: quux/lib.rs:
quux/foo.rs:
and then in a different crate
This will successfully compile, when it shouldn't. But if you run the executable compiled with |
@catamorphism why do you say that |
@nikomatsakis That seems weird to me (I would expect that nothing declared in a private module would be visible from another crate), but I wouldn't object to it. In that case, of course, the result should run without a dynamic loader error :-) |
@catamorphism yes, certainly. As for the matter of priv vs pub, I can certainly see your reasoning, it's an interesting difference between methods and items I hadn't thought too about before. From the standpoint of the *module that declares the method/item, there is little difference: |
This is the culmination and attempted resolution of #8215. The commits have many more details about implementation details and the consequences of this refinement. I'll point out specific locations which may be possible causes for alarm. In general, I have been very happy with how things have turned out. I'm a little sad that I couldn't remove privacy from resolve as much as I did, but I blame glob imports (although in theory even some of this can be mitigated as well).
This change was waiting for privacy to get sorted out, which should be true now that rust-lang#8215 has landed. Closes rust-lang#4427
So is it correct that if you implement a public function on a private struct, and call the function from an external crate, the compiler tells you that the function is private? Or should the compiler clarify that the struct is private? |
Making an issue off of my email to the mailing list.
Currently today resolve is generally considered "broken", and I think that this would benefit from actually writing the rules down, writing lots of test cases and documentation, and then fixing resolve. To the best of my knowledge, these are the rules which resolve should follow for determining whether an item is public or private and whether a certain path should resolve or not:
To flag an item as public, you use
pub
. Alsopub
is not allowed onimpl
or
extern
(visibility is determined by each item individually).item in the path is marked as
pub
.if each component of the path is
pub
. For example I could reach into achild's private mod's pub items, but not the child's private mod's non-pub
items.
I'm nominating for a backwards-compatibility milestone as there's probably lots of existing code which is doing the wrong thing by accident.
The text was updated successfully, but these errors were encountered: