Modules, Crates & Packages
Prerequisites: Level 1 — Beginner
In Level 1 every program lived in a single src/main.rs. Larger programs are
split into modules, and Rust has three words for how code is organized:
- A module (
mod) is a named scope for functions, types and other modules. It controls what is visible from outside. - A crate is one unit of compilation. A binary crate has a
mainfunction and its root file issrc/main.rs. A library crate has nomain, is meant to be used by other code, and its root file issrc/lib.rs. - A package is what
cargo newcreates: aCargo.tomlplus one or more crates. A package can contain at most one library crate and any number of binary crates.
Everything inside a module is private by default. Mark an item pub to
make it visible to the parent module and beyond. Struct fields are private
even when the struct itself is pub, so each field needs its own pub.
Enum variants are public whenever the enum is.
Syntax
mod network { // define a module inline
pub fn connect() {} // visible outside `network`
fn retry() {} // private to `network` and its children
}
use crate::network::connect; // bring a path into scope
// paths: crate:: (from the crate root), self:: (this module), super:: (parent)
Example
mod geometry {
pub mod shapes {
#[derive(Debug)]
pub struct Rectangle {
pub width: u32,
pub height: u32,
}
impl Rectangle {
pub fn area(&self) -> u32 {
self.width * self.height
}
}
pub fn square(side: u32) -> Rectangle {
Rectangle { width: side, height: side }
}
}
pub fn describe(r: &shapes::Rectangle) -> String {
format!("{}x{} (area {})", r.width, r.height, area_of(r))
}
// Private helper: only code inside `geometry` can call it.
fn area_of(r: &shapes::Rectangle) -> u32 {
r.area()
}
}
use geometry::shapes::{self, Rectangle};
fn main() {
let r = Rectangle { width: 3, height: 4 };
let s = shapes::square(5);
println!("{}", geometry::describe(&r));
println!("{}", geometry::describe(&s));
}
Expected output
3x4 (area 12)
5x5 (area 25)
Moving modules into files
Inline modules are fine for small programs. For real projects, replace the body with a semicolon and Cargo looks for the code in a file:
src/
├── main.rs mod geometry;
├── geometry.rs pub mod shapes; + describe() and area_of()
└── geometry/
└── shapes.rs Rectangle, square()
src/geometry.rs:
pub mod shapes;
pub fn describe(r: &shapes::Rectangle) -> String {
format!("{}x{} (area {})", r.width, r.height, area_of(r))
}
fn area_of(r: &shapes::Rectangle) -> u32 {
r.area()
}
src/geometry/shapes.rs contains exactly what was inside pub mod shapes { ... },
and src/main.rs starts with mod geometry;. The output is unchanged. The
older layout src/geometry/mod.rs also works, but src/geometry.rs is the
usual style today.
A mod declaration appears once, in the parent module. Other files reach
the module with use crate::geometry::... — never with a second mod.
lib.rs and main.rs together
A common layout is a package with both crate roots: the logic lives in the
library (src/lib.rs) and src/main.rs is a thin wrapper. The binary uses the
library by the package name, as if it were an external dependency:
// src/lib.rs in a package named `hello`
pub fn greet(name: &str) -> String {
format!("Hello, {name}!")
}
// src/main.rs in the same package
use hello::greet;
fn main() {
println!("{}", greet("Ada"));
}
Common mistakes
- Calling a private function from outside its module, such as
geometry::area_of(&r)inmain. The compiler reportserror[E0603]: function `area_of` is private. - Making a struct
pubbut not its fields, then building it outside the module:error[E0451]: field `width` of struct `Rectangle` is private. - Creating
src/geometry.rsbut forgettingmod geometry;inmain.rs. A file is not part of the crate until some module declares it, souse geometry::...fails witherror[E0433]: failed to resolve: use of undeclared crate or module `geometry`. - Writing
mod geometry;again in a second file to "import" it. That tells Rust to look for another file relative to that module. Useuseinstead.
Practice
In a package named calc, create src/lib.rs with a public module math
containing a public function double(n: i32) -> i32. Call it from
src/main.rs to print 42.
Show solution
src/lib.rs:
pub mod math {
pub fn double(n: i32) -> i32 {
n * 2
}
}
src/main.rs:
use calc::math::double;
fn main() {
println!("{}", double(21));
}