From 2c76cdae3e091ee8fe662713e89a56ceffc6e19c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 5 Oct 2013 17:07:57 -0700 Subject: [PATCH] Document visibility in the manual/tutorial This removes the warning "Note" about visibility not being fully defined, as it should now be considered fully defined with further bugs being considered just bugs in the implementation. --- doc/rust.md | 161 ++++++++++++++++++++++++++++++++++++++++++++++++ doc/tutorial.md | 25 ++++---- 2 files changed, 174 insertions(+), 12 deletions(-) diff --git a/doc/rust.md b/doc/rust.md index e998f97869f..98978e3e5a3 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -1501,6 +1501,167 @@ 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. + +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(); + } + } +} +~~~ + +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() {} +} +~~~ + +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} diff --git a/doc/tutorial.md b/doc/tutorial.md index 49ba38954b3..b2da355b122 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -2322,19 +2322,18 @@ fn main() { The `::farm::chicken` construct is what we call a 'path'. -Because it's starting with a `::`, it's also a 'global path', -which qualifies an item by its full path in the module hierarchy -relative to the crate root. +Because it's starting with a `::`, it's also a 'global path', which qualifies +an item by its full path in the module hierarchy relative to the crate root. -If the path were to start with a regular identifier, like `farm::chicken`, it would be -a 'local path' instead. We'll get to them later. +If the path were to start with a regular identifier, like `farm::chicken`, it +would be a 'local path' instead. We'll get to them later. -Now, if you actually tried to compile this code example, you'll notice -that you get a `unresolved name: 'farm::chicken'` error. That's because per default, -items (`fn`, `struct`, `static`, `mod`, ...) are only visible inside the module -they are defined in. +Now, if you actually tried to compile this code example, you'll notice that you +get a `function 'chicken' is private` error. That's because by default, items +(`fn`, `struct`, `static`, `mod`, ...) are private. -To make them visible outside their containing modules, you need to mark them _public_ with `pub`: +To make them visible outside their containing modules, you need to mark them +_public_ with `pub`: ~~~~ mod farm { @@ -2356,7 +2355,8 @@ Rust doesn't support encapsulation: both struct fields and methods can be private. But this encapsulation is at the module level, not the struct level. -For convenience, fields are _public_ by default, and can be made _private_ with the `priv` keyword: +For convenience, fields are _public_ by default, and can be made _private_ with +the `priv` keyword: ~~~ mod farm { @@ -2393,7 +2393,8 @@ fn main() { # fn make_me_a_chicken() -> farm::Chicken { 0 } ~~~ -> ***Note:*** Visibility rules are currently buggy and not fully defined, you might have to add or remove `pub` along a path until it works. +Exact details and specifications about visibility rules can be found in the Rust +manual. ## Files and modules