Modules & Crates
Course 1 stayed within single small files. This chapter covers organizing a real Rust project as it grows: modules within a crate, Cargo.toml in depth, workspaces for multi-crate projects, and finally publishing a crate to crates.io.
The Module System
mod organizes code into a tree of namespaces within a single crate. Everything is private by default — a genuinely different default from many languages. pub makes an item visible to its parent module — not globally, a common early misconception. pub(crate) exposes an item crate-wide, but not to external code.
Splitting Modules Across Files
A module can live in its own file: mod front_of_house; (no body) tells Rust to look for the module's contents elsewhere. The modern convention (2018 edition onward) needs no mod.rs file:
use and Bringing Paths Into Scope
use shortens repeated long paths — already in constant use since Course 1's use std::collections::HashMap (Chapter 8). pub use re-exports an item, making something defined deep in the module tree part of the crate's own public API surface at a shallower, more convenient path.
Cargo.toml in Depth
Cargo.lock pins the exact, fully-resolved dependency versions actually used — directly analogous to npm's package-lock.json. Committing it for a binary/application ensures reproducible builds across machines; libraries conventionally don't commit it, since consumers resolve their own compatible versions.
Workspaces: Multiple Crates, One Project
A workspace shares one Cargo.lock and one target/ build directory across every member crate — the standard pattern once a project grows past a single crate, e.g. splitting a binary crate from the library crate(s) it depends on.
Publishing to crates.io
cargo publish requires a globally unique crate name (crates.io names can't be reused once claimed), required metadata (license, description), and an API token. Publishing is permanent for a given version number — a published version can be yanked (hidden from new dependents) but never overwritten or deleted outright.
| npm (JavaScript) | Cargo (Rust) | |
|---|---|---|
| Lock file | package-lock.json | Cargo.lock |
| Purpose | Pin exact resolved versions for reproducibility | Identical purpose |
| Committed for libraries? | Typically not | Typically not (unlike applications, which do) |
mod / pub
Organizes code into namespaces; private by default, pub exposes to the parent module.
File-Based Modules
mod name; points at name.rs, with name/submodule.rs for its own children.
Cargo.toml / Cargo.lock
Package metadata and dependencies; the lock file pins exact resolved versions.
Workspaces
Multiple crates sharing one lock file and build directory under one root Cargo.toml.
pub is the same "safety by default" instinct behind Course 1's immutable-by-default variables — an accidentally-exposed item requires deliberately opting in with pub, rather than requiring someone to remember to lock it down after the fact.
pub only exposes it to its immediate parent module — it does not automatically make it reachable from outside the crate. Every module along the path to that item must also be pub (or the item must be re-exported via pub use at a more accessible path). Visibility has to be "pub all the way up," not just at the final leaf item — a genuinely common early confusion.
Coding Challenges
Write a module tree: a top-level module shop containing a submodule inventory with a public function check_stock(). Show the full path needed to call check_stock() from outside the shop module, and explain what pub keywords are required at each level.
📄 View solutionWrite a Cargo.toml for a binary crate named "task_tracker" at version 0.1.0, using the 2021 edition, depending on serde version "1.0" and clap version "4.0". Explain what Cargo.lock would add on top of this file once the project is built.
📄 View solutionExplain the specific bug in this module tree: mod a { pub mod b { pub fn f() {} } } where a itself is NOT marked pub — why can code outside the crate still not call a::b::f() from outside, even though both b and f are marked pub?
📄 View solutionChapter 6 Quick Reference
- mod name { ... } — declares a module; items are private by default
- pub — exposes an item to its parent module; pub(crate) — exposes crate-wide only
- mod name; (no body) — loads the module from name.rs, with name/ for its own submodules
- use path::to::item; — shortens repeated paths; pub use — re-exports at a shallower path
- Cargo.toml — package metadata + dependencies; Cargo.lock — exact resolved versions, like npm's package-lock.json
- [workspace] members = [...] — multiple crates sharing one lock file and build directory
- cargo publish — permanent per version; a bad publish can only be yanked, never overwritten
- pub must apply all the way up the module path, not just at the final item, for external code to actually reach it
- Next chapter: Testing in Rust — #[test], cargo test, unit vs. integration tests, contrasted with Go's built-in testing package