Modules, Visibility, and Re-exports
Rule
Keep modules private by default, expose focused facades, and give each local item one intended public path.
Why
Small public surfaces protect invariants and let implementations move without breaking callers.
Do
- Keep implementation modules private unless their path is part of the API.
- Use
pub(crate)for real internal boundaries andpub(super)for tight parent-child collaboration. - Re-export the public concepts callers should name from the crate root or a focused facade.
- Choose either a facade path or a public module path as canonical for each local item.
- Use
#[doc(inline)]when a re-export from a public module or another crate should appear at the facade path. - Follow struct design for field visibility and naming/imports for preludes.
Avoid
- Do not expose deep module paths accidentally.
- Do not use
pubwhenpub(crate)is enough. - Do not re-export every internal type from the crate root.
- Do not expose the same local item through multiple accidental public paths.
- Do not let implementation churn define public module structure.
Public API Notes
Published libraries treat every intended pub path as compatibility surface. Binary crates normally need only private or pub(crate) boundaries.
Example
#![allow(unused)]
fn main() {
// lib.rs
mod client;
mod error;
mod request;
pub use client::Client;
pub use error::ClientError;
pub use request::Request;
}
Exceptions
- Use
pub modwhen the module is an intentional stable namespace. - Keep an additional public path only for documented compatibility or a deliberate ergonomic alias.