Visibility

From Rust Community Wiki
Jump to navigation Jump to search

See also: Reference – Visibility and Privacy

Visibility controls from where items and fields can be accessed and is specified with the pubThis links to official Rust documentation keyword. It is the core mechanism to enable encapsulation in the module system.

Encapsulation means that every crate and every module exposes a public API, whereas implementation details are hidden and can be modified freely without breaking backwards compatibility. If something is private, you can rest assured that it will never be used outside of its module. Encapsulation enables local reasoning: The implementation details of one module don't affect the implementation details of another module. This is crucial for the correctness of both safe and unsafe code.

One example of this are the fields of the VecThis links to official Rust documentation type. If the fields could be modified from anyone, they could be set to incorrect values, leading to Undefined Behavior and possibly security vulnerabilities. But since the fields are private, they can only be modified in the std::vec module. Because this code has been carefully reviewed, we can be confident that the fields of a Vec are always correct.

Accessibility and visibility of items[edit | edit source]

If an item is visible, that doesn't imply that it can be accessed. An item is accessible in a module m if two conditions are satisfied:

  • The item is visible in m
  • A path to the item exists that is visible in m

If no visible path to the item exists, it is inaccessible, even if the item itself is visible. The visibility is only the maximum scope where the item can be accessed:

pub mod public {
    mod private {
        pub struct S1;
        pub struct S2; 
    }

    pub use private::S1;
}
// S1 is accessible here, but not S2

In this example, both S1 and S2 are public, but S2 isn't publicly exported. The path public::private::S2 can't be accessed, because the private module isn't visible outside of the enclosing module.

Default visibility[edit | edit source]

Most items and fields are private by default; this visibility is chosen when no visibility modifier is specified. Private items and fields can only be accessed within the current module (including its descendants). It can not be accessed in parent or sibling modules, or in different crates:

mod a {
    struct S;
    // S is visible here

    mod b {
        // and here
    }
}
// S is NOT visible here

There are a few exceptions, however. Enum variants, enum fields, macros and associated items of public traits are always public. They can't be made private, so they are always accessible, if they are exported:

mod a {
    pub enum Enum {
        Variant,  // this is public!
    }

    #[macro_export]
    macro_rules! my_macro { () => {} }  // this is public!
}

use a::Enum::Variant;
my_macro!();

Public visibility[edit | edit source]

If an item or field is public, it is visible everywhere in the current crate, as well as outside of the crate. Items are made public by adding the pubThis links to official Rust documentation keyword, which is called a visibility modifier or visibility qualifier:

// crate a
pub mod inner {
    pub struct S;
}
// crate b
use a::inner::S;

Visibility in a specific module[edit | edit source]

All items or fields that aren't public are visible in exactly one module. For example, private items are only visible in the module where they were defined. The visibility is specified with the pub(in PATH) syntax, where PATH is a path to the current module (self) or any of its ancestors:

pub(in self) struct S1;

pub(in self) mod a {
    pub(in super) struct S2;

    pub(in self) mod b {
        pub(in crate) struct S3;
        pub(in crate::a) struct S4;
    }
}

Since this is quite verbose, it can be abbreviated:

  • pub(in crate), pub(in super) and pub(in self) can be abbreviated with pub(crate), pub(super) and pub(self), respectively. These three visibility modifiers are special-cased, because they are so common.
  • pub(in self) means that the item or field is private (it is only visible in the current module), which is the default, so the visibility modifier can be omitted.

As a result, the above is equivalent to this code:

struct S1;

mod a {
    pub(super) struct S2;

    mod b {
        pub(crate) struct S3;
        pub(in crate::a) struct S4;
    }
}

Note that the specified path must refer to the current module or one of its ancestors:

mod a {}

mod b {
    pub(in crate::a) struct S;
}

error[E0742]: visibilities can only be restricted to ancestor modules

Visibility and exporting[edit | edit source]

Items are exported with the useThis links to official Rust documentation keyword (except macros). Rust prevents you from exporting an item to a module where it isn't visible:

struct S;
pub use S as Exported;

error[E0364]: `S` is private, and cannot be re-exported

However, it doesn't prevent you from making an item public, even if it isn't exported anywhere:

mod private {
    pub struct S;
}

pub fn return_s() -> private::S {
    private::S
}

The above code contains a public function returning a public struct. However, the struct is in a private module, which means that it is not accessible outside of this module. Therefore, the function return_s() can be called from anywhere, but it's return type can't be named outside of this crate:

let _ = return_s(); // works

let _: some_crate::private::S = return_s(); // error

error[E0603]: module `private` is private

Note that S must be public in order to be used in a public function:

struct S;

pub fn return_s() -> S { S }

error[E0446]: private type `S` in public interface

This ensures that implementation details never “leak“ into the public API: Anything that can be observed from another crate must be public.

Visibility modifiers in macros[edit | edit source]

To match a visibility modifier in a macro, macro_rules! has a vis metavariable:

macro_rules! func {
    ($v:vis $name:ident) => {
        $v fn $name () {}
    }
}
func!(pub(crate) noop);

Historic note about in[edit | edit source]

The in keyword of pub(in PATH) can't be omitted, unless PATH is either crate, super or self. Although RFC 1422: pub restricted proposed the syntax pub(PATH), this would have caused syntactic ambiguity, so it was decided to use the more explicit pub(in PATH) syntax.[1]

Experimental features[edit | edit source]

RFC 2126: path clarity allows replacing pub(crate) with just crate. The RFC has been accepted and implemented, but the crate visibility modifier is still an unstable feature:

#![feature(crate_visibility_modifier)]

crate struct Foo;

The motivation for this is to make code more concise and readable.

References[edit | edit source]