-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Define and Document what visibility really means #9735
Changes from all commits
439e277
de7d143
2c76cda
7cd6692
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1501,6 +1501,171 @@ is `extern "abi" fn(A1, ..., An) -> R`, | |
where `A1...An` are the declared types of its arguments | ||
and `R` is the decalred return type. | ||
|
||
## Visibility and Privacy | ||
|
||
These two terms are often used interchangeably, and what they are attempting to | ||
convey is the answer to the question "Can this item be used at this location?" | ||
|
||
Rust's name resolution operates on a global hierarchy of namespaces. Each level | ||
in the hierarchy can be thought of as some item. The items are one of those | ||
mentioned above, but also include external crates. Declaring or defining a new | ||
module can be thought of as inserting a new tree into the hierarchy at the | ||
location of the definition. | ||
|
||
To control whether interfaces can be used across modules, Rust checks each use | ||
of an item to see whether it should be allowed or not. This is where privacy | ||
warnings are generated, or otherwise "you used a private item of another module | ||
and weren't allowed to." | ||
|
||
By default, everything in rust is *private*, with two exceptions. The first | ||
exception is that struct fields are public by default (but the struct itself is | ||
still private by default), and the remaining exception is that enum variants in | ||
a `pub` enum are the default visibility of the enum container itself.. You are | ||
allowed to alter this default visibility with the `pub` keyword (or `priv` | ||
keyword for struct fields and enum variants). When an item is declared as `pub`, | ||
it can be thought of as being accessible to the outside world. For example: | ||
|
||
~~~ | ||
// Declare a private struct | ||
struct Foo; | ||
|
||
// Declare a public struct with a private field | ||
pub struct Bar { | ||
priv field: int | ||
} | ||
|
||
// Declare a public enum with public and private variants | ||
pub enum State { | ||
PubliclyAccessibleState, | ||
priv PrivatelyAccessibleState | ||
} | ||
~~~ | ||
|
||
With the notion of an item being either public or private, Rust allows item | ||
accesses in two cases: | ||
|
||
1. If an item is public, then it can be used externally through any of its | ||
public ancestors. | ||
2. If an item is private, it may be accessed by the current module and its | ||
descendants. | ||
|
||
These two cases are surprisingly powerful for creating module hierarchies | ||
exposing public APIs while hiding internal implementation details. To help | ||
explain, here's a few use cases and what they would entail. | ||
|
||
* A library developer needs to expose functionality to crates which link against | ||
their library. As a consequence of the first case, this means that anything | ||
which is usable externally must be `pub` from the root down to the destination | ||
item. Any private item in the chain will disallow external accesses. | ||
|
||
* A crate needs a global available "helper module" to itself, but it doesn't | ||
want to expose the helper module as a public API. To accomplish this, the root | ||
of the crate's hierarchy would have a private module which then internally has | ||
a "public api". Because the entire crate is an ancestor of the root, then the | ||
entire local crate can access this private module through the second case. | ||
|
||
* When writing unit tests for a module, it's often a common idiom to have an | ||
immediate child of the module to-be-tested named `mod test`. This module could | ||
access any items of the parent module through the second case, meaning that | ||
internal implementation details could also be seamlessly tested from the child | ||
module. | ||
|
||
In the second case, it mentions that a private item "can be accessed" by the | ||
current module and its descendants, but the exact meaning of accessing an item | ||
depends on what the item is. Accessing a module, for example, would mean looking | ||
inside of it (to import more items). On the other hand, accessing a function | ||
would mean that it is invoked. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn’t There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, I believe that you are correct, I'll write up a little extra about re-exportations as well. |
||
|
||
Here's an example of a program which exemplifies the three cases outlined above. | ||
|
||
~~~ | ||
// This module is private, meaning that no external crate can access this | ||
// module. Because it is private at the root of this current crate, however, any | ||
// module in the crate may access any publicly visible item in this module. | ||
mod crate_helper_module { | ||
|
||
// This function can be used by anything in the current crate | ||
pub fn crate_helper() {} | ||
|
||
// This function *cannot* be used by anything else in the crate. It is not | ||
// publicly visible outside of the `crate_helper_module`, so only this | ||
// current module and its descendants may access it. | ||
fn implementation_detail() {} | ||
} | ||
|
||
// This function is "public to the root" meaning that it's available to external | ||
// crates linking against this one. | ||
pub fn public_api() {} | ||
|
||
// Similarly to 'public_api', this module is public so external crates may look | ||
// inside of it. | ||
pub mod submodule { | ||
use crate_helper_module; | ||
|
||
pub fn my_method() { | ||
// Any item in the local crate may invoke the helper module's public | ||
// interface through a combination of the two rules above. | ||
crate_helper_module::crate_helper(); | ||
} | ||
|
||
// This function is hidden to any module which is not a descendant of | ||
// `submodule` | ||
fn my_implementation() {} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
|
||
#[test] | ||
fn test_my_implementation() { | ||
// Because this module is a descendant of `submodule`, it's allowed | ||
// to access private items inside of `submodule` without a privacy | ||
// violation. | ||
super::my_implementation(); | ||
} | ||
} | ||
} | ||
|
||
# fn main() {} | ||
~~~ | ||
|
||
For a rust program to pass the privacy checking pass, all paths must be valid | ||
accesses given the two rules above. This includes all use statements, | ||
expressions, types, etc. | ||
|
||
### Re-exporting and Visibility | ||
|
||
Rust allows publicly re-exporting items through a `pub use` directive. Because | ||
this is a public directive, this allows the item to be used in the current | ||
module through the rules above. It essentially allows public access into the | ||
re-exported item. For example, this program is valid: | ||
|
||
~~~ | ||
pub use api = self::implementation; | ||
|
||
mod implementation { | ||
pub fn f() {} | ||
} | ||
|
||
# fn main() {} | ||
~~~ | ||
|
||
This means that any external crate referencing `implementation::f` would receive | ||
a privacy violation, while the path `api::f` would be allowed. | ||
|
||
When re-exporting a private item, it can be thought of as allowing the "privacy | ||
chain" being short-circuited through the reexport instead of passing through the | ||
namespace hierarchy as it normally would. | ||
|
||
### Glob imports and Visibility | ||
|
||
Currently glob imports are considered an "experimental" language feature. For | ||
sanity purpose along with helping the implementation, glob imports will only | ||
import public items from their destination, not private items. | ||
|
||
> **Note:** This is subject to change, glob exports may be removed entirely or | ||
> they could possibly import private items for a privacy error to later be | ||
> issued if the item is used. | ||
|
||
## Attributes | ||
|
||
~~~~~~~~{.ebnf .gram} | ||
|
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.
Isn’t the entire crate a descendant of the root?
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.
Ah yes, good catch! I'll roll this in with a pull coming up soon.