Packages, Crates, and Modules
Rust's hierarchy for growing code: package → crate → module tree, and the privacy rules that make it work.
Part of Rust Interview Mastery · Based on The Rust Programming Language
Package, Crate, Module
Define package, crate, and module — how do they nest?
- Package — a Cargo.toml + source; what cargo new makes. Contains 1+ crates, at most one library crate.
- Crate — the compilation unit. A binary crate (src/main.rs) or library crate (src/lib.rs) — the file is the crate root.
- Module — mod blocks inside a crate that organize items and control privacy.
So: cargo new → package → crate root file → mod tree inside it. Terms like "the rand crate" mean a library crate from a package.
What is the crate root, and how do modules map to files?
The crate root (src/main.rs or src/lib.rs) is where the module tree starts. Declaring mod garden; makes the compiler look for src/garden.rs or src/garden/mod.rs. Nested modules mirror the tree:
// src/main.rs mod garden; // -> src/garden.rs // src/garden.rs mod vegetables; // -> src/garden/vegetables.rs
Same-source-file alternative: mod garden { ... } inline. Files vs. inline is layout; the mod keyword is what builds the tree.
What's the difference between a binary crate and a library crate?
A binary crate compiles to an executable and needs fn main. A library crate compiles to code others depend on — no main.
One package can contain one library (src/lib.rs) plus any number of binaries (src/main.rs, src/bin/*.rs). The standard pattern: keep logic in lib.rs, make main.rs a thin shell — you get testability and let other crates reuse the logic.
Paths, Privacy & use
Explain absolute vs relative paths, self, and super.
crate::front_of_house::hosting::add_to_waitlist(); // absolute from root front_of_house::hosting::add_to_waitlist(); // relative to here self::tools::wrench(); // explicit relative super::serve_order(); // up one level
Absolute (crate::) is stable when you move the calling code — the path doesn't change. Relative is shorter when caller and callee live together. super:: mirrors .. in filesystems — the book uses it for a serve_order that lives one level up.
State Rust's privacy rules — what can see what by default?
Everything is private by default. An item is visible to:
- its own module and descendants (children can see parent's private items)
- nobody outside the module — parents can't see children's privates, siblings can't see each other's
pub flips it. Key subtleties: pub struct still keeps fields private unless each is pub; pub enum makes all variants public; a pub fn can only be called through a path whose modules are all reachable. Privacy is about paths, which is why the restaurant example's pub mod hosting matters as much as pub fn.
How do you idiomatically use the use keyword — and what's the convention for different item kinds?
use brings a path into scope so you write the short name:
use crate::front_of_house::hosting; hosting::add_to_waitlist(); // fn: bring the MODULE, keep it explicit use std::collections::HashMap; let m = HashMap::new(); // struct/enum: bring the TYPE itself use std::io::Result as IoResult; // 'as' to disambiguate
Convention: functions get imported to their parent module (shows where they live); structs/enums/traits get imported by name; collisions get as renames. It's a style norm, not a rule — the goal is call sites that read clearly.
What does pub use do and when do you reach for it?
pub use re-exports: it makes an item appear at a path you control, decoupled from where it's defined:
// deep internal layout
mod storage { pub mod kv { pub struct Cache; } }
pub use storage::kv::Cache; // users write mycrate::Cache
You reach for it when your internal module structure is messy but you want a clean public API — crate::Cache looks flat to users even though implementation lives three levels deep. Standard library does this constantly.