Skip to content

← Modules, Visibility, Testing and Docs step 4 of 22

Medium Primitives

Restricted visibility: pub(crate), pub(super), pub(in path)

pub and private are the two everybody learns. Real Rust codebases are held together by the three in between.

pub(crate)          // visible anywhere in this crate, nowhere outside it
pub(super)          // visible from the parent module and everything under it
pub(in crate::a::b) // visible from crate::a::b and everything under it

The Book barely mentions pub(crate), and it is probably the single most useful visibility a beginner can learn: it says “this is shared infrastructure, not part of my public API”, which is a distinction pub cannot express.

The task

Write the oracle.

pub fn visible_from(items: Vec<(String, String)>, viewer: String, target: String) -> bool

items describes a whole crate: every entry is a (path, visibility) pair, and modules are entries too — that is the point. viewer is the module doing the looking, target the item being looked at. Visibilities are the strings "private", "pub", "pub(crate)", "pub(super)" and "pub(in crate::a::b)". An unknown target is false, not a panic.

Both privacy rules have to be implemented, including the descendant clause:

A public item is accessible from m only if every ancestor module of it is also accessible from m.

A private item is accessible from its declaring module and all of its descendants.

So you walk the chain from the item up to the crate root and ask, at every link, whether that link admits the viewer. One private module anywhere on the chain and nothing below it can be named from outside.

The trap, and it is a good one

pub(super) on an item inside a top-level module is effectively crate-visible. Learners read pub(super) as “only my parent can see this” and it is not what it says.

Work it through. secret is declared in crate::a, so super is crate — the crate root. pub(in crate) means “visible from crate and all of its descendants”, and every module in the crate is a descendant of the crate root. So crate::b::c can see it. The restriction restricted nothing.

pub(super) only narrows anything at depth two or more. This is not a quirk to memorise; it falls straight out of “visibility is reachability in a tree”, which is why implementing the oracle teaches it better than reading about it.

pub(in path) and E0742

The path in pub(in …) must name an ancestor of the item’s own module. pub(in crate::unrelated) is E0742 — visibilities can only be restricted to ancestor modules. You cannot hand a capability sideways; visibility is a prefix of the tree, not an access-control list. Your inputs are always well-formed in that respect.

Two clippy lints that flatly contradict each other

  • clippy::pub_with_shorthand wants you to write pub(super).
  • clippy::pub_without_shorthand wants you to write pub(in super).

Both are restriction lints, both are off by default, and enabling both is incoherent. They are in the list on purpose: clippy has opinions, curated by teams with policies, not truths handed down from the language. When a lint tells you to change something, the question is always “do I agree”, not “how do I comply”.

One more, clippy::redundant_pub_crate: pub(crate) inside a module that is already private is redundant, and the lint says so. It is a nursery lint, so it is off here, but the observation is sound and worth internalising.

A caveat about this harness

Your submission is one file and therefore one crate, and it produces a binary, not a library. Inside a single crate pub and pub(crate) are almost interchangeable as far as compiling goes — the difference only shows up when somebody outside the crate tries to use your code, which cannot happen here. That is precisely why this item is an oracle problem rather than a “make it compile” problem. The rules are real; the only way to exercise them on one file is to implement them.