Definitive guide on `pub` keyword visibility?

Hi -- I've been trying to understand the visibility of structs in rust and what the "pub" keyword does. By experimenting, I have reversed-engineered some rules but I couldn't verify them. Is there a pointer to a definitive guide so I can check if my findings are correct?

My current version of the rules:

  • A non-public struct is visible to the module where it's defined and all its descendants
  • A public struct is only visible to the immediate parent of a module, but not higher.

Is this correct?

Say we have module tree structure being
Crate -> (Mod1, Mod2), and Mod1 -> (SubMod1, SubMod2).

  • If we have a struct Foo in Mod1, then it is visible from SubMod1, but not visible in any other places.

  • If we have a pub struct Bar in the lowest submodule SubMod1, then it is visible from Mod1 (its immediate parent), but not visible anywhere else.

Please advise. Thanks!

The definitive reference is here: https://doc.rust-lang.org/reference/visibility-and-privacy.html

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 accessed externally from some module m if you can access all the item's ancestor modules from m . You can also potentially be able to name the item through re-exports. See below.
  2. If an item is private, it may be accessed by the current module and its descendants.

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. Additionally, path expressions and import statements are considered to access an item in the sense that the import/expression is only valid if the destination is in the current visibility scope.

5 Likes

A pub item inside module x is visible in any place where module x is visible. This always includes x’s parent module (including its descendants), but if x is also pub, it can include other modules too.

Thanks! I've a question on the point2. Say I have a nested module structure A->B->C->D, and I have a private struct defined in B. Then what's the syntax to access it in D? I know structs in C can be accessed via super keyword, but what about the module B which is two level up?

You can keep adding supers to the path: super::super::SomeBStruct. If you're familar with unix file paths you can think of it like one, where super is ../, crate is / and self is ./

3 Likes

You can also use the absolute path crate::A::B::SomeBStruct which is the same no matter where you use it.

1 Like

Note also that re-exports are important here. If you have a public type in a private module, the parent module can still re-export it and make it available in other places.

Somewhat related, but, if you enable -W unreachable-pub warning, the semantics of pub becomes clearer -- pub are the things visible outside of the crate, they are exported public API.

Yes -- but isn't that only okay if I'm calling it in a descendant (e.g. D) but not okay if I call it from a sibling module (say A'), due to mod A is private?

Interesting! could you give an example of re-export?

Cool! Where should I put the -W unreachable-pub?

A private item is visible everywhere within its parent module. This means that a private module is visible to its siblings.

Public/private only controls whether an item is "exported" beyond its parent module. (At the top level of a crate, it only controls whether the item is visible to external crates.)

Yeah sorry I didn't express myself clearly: I meant say we have crate->A->B->C->D. and B has a private struct Foo. Say we also have crate->A'. Then we can use Foo in D by use crate::A::B::Foo, but the same command will fail in A', is that right?

A reexport is written like this:

// mod1.rs

pub use self::submod1::Bar;

Now you can access Bar as if it was defined in Mod1.

If you really choose to use it (which imo makes sense for larger projects, but is not worth it for the smaller ones), I'd go with setting RUSTFLAGS="-W unreachable-pub" environment variable on CI.

You can also use this attribute in the crate root(lib.rs/main.rs): #![warn(unreachable_pub)].

Right.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.