Working Code
As code grows, putting everything in one file gets messy. Modules let you organize code into logical groups.
mod math {
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
pub fn multiply(a: i32, b: i32) -> i32 {
a * b
}
fn secret_formula(x: i32) -> i32 {
x * 42 // private function!
}
}
fn main() {
let sum = math::add(3, 5);
let product = math::multiply(4, 6);
println!("3 + 5 = {sum}");
println!("4 x 6 = {product}");
// math::secret_formula(1); // error! private
}
mod math creates a module. You call its functions with the path math::add().
Try It Yourself
- Uncomment
math::secret_formula(1). What error do you get? - Add
pubtosecret_formula. Does the error go away?
"Why?" — pub and Private by Default
In Rust, items inside a module are private by default. You must explicitly add pub to make them accessible from outside.
This follows the same philosophy as ownership — safety is the default. Expose only what's necessary and hide the rest.
| Keyword | Meaning |
| ------- | ------------------------------------------------ |
| (none) | Private — accessible only within the same module |
| pub | Public — accessible from outside |
use — Shorter Paths
If you want to skip writing math::add() every time, use use:
mod math {
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
pub fn multiply(a: i32, b: i32) -> i32 {
a * b
}
}
use math::add;
use math::multiply;
fn main() {
println!("3 + 5 = {}", add(3, 5));
println!("4 x 6 = {}", multiply(4, 6));
}
You can import multiple items at once:
use math::{add, multiply};
Splitting Modules Into Files
As code grows further, you can move modules to separate files. This is the most common pattern in real projects.
src/
├── main.rs
└── math.rs
src/math.rs:
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
pub fn multiply(a: i32, b: i32) -> i32 {
a * b
}
src/main.rs:
mod math; // loads the math.rs file as a module
fn main() {
println!("3 + 5 = {}", math::add(3, 5));
}
Just write mod math; and Rust finds and links the math.rs file automatically.
Using External Crates
You used the rand crate in Lesson 07. External crates are available on crates.io.
cargo add rand
This command automatically adds the crate to the [dependencies] section of Cargo.toml:
[dependencies]
rand = "0.8"
Then import and use it:
use rand::Rng;
fn main() {
let mut rng = rand::thread_rng();
let number: i32 = rng.gen_range(1..=100);
println!("Random number: {number}");
}
Standard Library Modules
Rust comes with a standard library (std) full of commonly used features. Import them with use — no installation needed.
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 95);
scores.insert("Bob", 88);
for (name, score) in &scores {
println!("{name}: {score}");
}
}
Commonly used standard library modules:
| Module | Purpose |
| ------------------ | ----------------------- |
| std::io | Input/output |
| std::collections | HashMap, BTreeMap, etc. |
| std::fs | File read/write |
| std::fmt | Formatting |
Learn More: Finding Crates on crates.io
crates.io is Rust's package registry with tens of thousands of crates.
Here are some popular ones:
| Crate | Purpose |
| --------- | ----------------------------------------------- |
| serde | Data serialization/deserialization (JSON, etc.) |
| tokio | Async runtime |
| clap | Command-line argument parsing |
| reqwest | HTTP requests |
When choosing a crate, check its download count and last update date. Actively maintained crates are the safest bet.
Learn More: Module Paths and the crate Keyword
Module paths have two starting points:
// Absolute path: starts from crate
crate::math::add(1, 2);
// Relative path: from current location
math::add(1, 2);
// Parent module reference
super::some_function();
crate is a keyword pointing to the root of the current project. For large projects with complex paths, absolute paths can be clearer.
Exercise 1. Create a geometry module with circle_area and rectangle_area functions.
mod geometry {
// circle_area: takes radius, returns area of a circle
// rectangle_area: takes width and height, returns area
// Both should be pub!
// Hint: PI is std::f64::consts::PI
}
fn main() {
println!("Circle: {:.2}", geometry::circle_area(5.0));
println!("Rectangle: {:.2}", geometry::rectangle_area(4.0, 6.0));
}
Exercise 2. Use use to shorten the code below.
fn main() {
let mut map = std::collections::HashMap::new();
map.insert("a", 1);
map.insert("b", 2);
let mut set = std::collections::HashSet::new();
set.insert("x");
set.insert("y");
println!("{:?}", map);
println!("{:?}", set);
}
Hint: use std::collections::{HashMap, HashSet};
Q1. Functions inside a Rust module are, by default:
- A) Public
- B) Private
- C) Read-only
- D) Constant
Q2. The command to add an external crate to a project is:
- A)
cargo install crate - B)
cargo add crate - C)
cargo get crate - D)
cargo import crate
Q3. Why does this code produce an error?
mod secrets {
fn hidden() -> i32 { 42 }
}
fn main() {
println!("{}", secrets::hidden());
}
- A) The
modsyntax is wrong - B) The
hiddenfunction is private - C) The return type is wrong
- D) The
secretsmodule doesn't exist