Every project beyond one file needs modules, and every real project depends on crates from crates.io.
A package is what cargo new creates: a Cargo.toml plus one or more crates. A crate is a compilation unit, either a binary (rooted at src/main.rs) or a library (rooted at src/lib.rs). A package can have both; the binary then uses the library by its package name.
Inside a crate, code is organised into a tree of modules. mod parser; in main.rs or lib.rs tells the compiler to load src/parser.rs (or src/parser/mod.rs). Submodules of parser live in src/parser/. You can also write a module inline: mod tests { ... }.
Everything is private by default to its module (and that module's children). Mark items pub to expose them; pub(crate) exposes within the crate only. For structs, each field needs its own pub; for enums, making the enum pub makes all variants public.
Paths: crate::parser::parse is absolute from the crate root, super:: goes up one level, self:: is the current module. use brings names into scope; idiomatic style imports the parent module for functions (use std::fs; then fs::read) and the item itself for types (use std::collections::HashMap;). pub use re-exports, letting you present a flatter public API than your file layout.
Dependencies go in Cargo.toml under [dependencies], e.g. serde = { version = "1", features = ["derive"] }, or simply run cargo add serde --features derive. Version requirements are semver ranges: "1.4" means at least 1.4.0 and below 2.0.0. Cargo.lock records exact versions; commit it for applications. [dev-dependencies] are only for tests and examples. Large projects group crates in a workspace sharing one lock file and target/ directory.
Example
// src/lib.rs
pub mod shapes {
#[derive(Debug)]
pub struct Circle {
pub radius: f64,
id: u32, // private: only `shapes` and its child modules can touch it
}
impl Circle {
pub fn new(radius: f64) -> Self { Self { radius, id: next_id() } }
}
fn next_id() -> u32 { 1 } // private helper
pub mod util {
pub fn describe(c: &super::Circle) -> String {
format!("circle #{} r={}", c.id, c.radius) // child module sees parent's private items
}
}
}
pub use shapes::Circle; // re-export: callers can write my_crate::Circle
// src/main.rs: use my_crate::{Circle, shapes::util::describe};Remember
- A package holds crates; a crate is a tree of modules rooted at main.rs or lib.rs
- Items and struct fields are private unless marked
pub cargo addand[dependencies]pull crates from crates.io using semver ranges
Watch out for
- Writing
mod foo;in every file that usesfoo; declare a module once, thenuseit elsewhere.