Intermediate Level
Modules and Packages
Chapter 10: Modules and Packages š¦
Organize your Rust code with modules, crates, and packages for better maintainability and reusability.
Modules
Defining Modules
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
fn main() {
// Absolute path
crate::front_of_house::hosting::add_to_waitlist();
// Relative path
front_of_house::hosting::add_to_waitlist();
}Moving Modules to Separate Files
Create front_of_house.rs:
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}Or create front_of_house/mod.rs:
pub mod hosting;
pub mod serving;And front_of_house/hosting.rs:
pub fn add_to_waitlist() {
// Implementation
}
fn seat_at_table() {
// Implementation (private)
}Paths
Absolute vs Relative Paths
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
// Absolute path
fn serve_at_absolute_path() {
crate::front_of_house::hosting::add_to_waitlist();
}
// Relative path
fn serve_at_relative_path() {
front_of_house::hosting::add_to_waitlist();
}
fn main() {
serve_at_absolute_path();
serve_at_relative_path();
}The use Keyword
Bringing Paths into Scope
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting::add_to_waitlist;
fn main() {
add_to_waitlist();
add_to_waitlist();
}Using use with Relative Paths
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use front_of_house::hosting::add_to_waitlist;
fn main() {
add_to_waitlist();
add_to_waitlist();
}Using use with Modules
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
fn main() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
}Renaming with as
Avoiding Name Conflicts
use std::fmt::Result;
use std::io::Result as IoResult;
fn function1() -> Result {
// Implementation
}
fn function2() -> IoResult<()> {
// Implementation
}Re-exporting with pub use
Making Paths Public
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub use crate::front_of_house::hosting;
fn main() {
hosting::add_to_waitlist();
}Using External Crates
Adding Dependencies to Cargo.toml
[dependencies]
rand = "0.8.5"
serde = { version = "1.0", features = ["derive"] }Using External Crates in Code
use rand::Rng;
fn main() {
let secret_number = rand::thread_rng().gen_range(1..=100);
println!("Secret number: {}", secret_number);
}The pub Keyword
Public vs Private
mod back_of_house {
pub enum Appetizer {
Soup,
Salad,
}
pub struct Breakfast {
pub toast: String,
seasonal_fruit: String, // Private
}
impl Breakfast {
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peaches"),
}
}
}
fn fix_incorrect_order() {
cook_order();
}
fn cook_order() {} // Private
}
pub fn eat_at_restaurant() {
// Order an appetizer
let order1 = back_of_house::Appetizer::Soup;
let order2 = back_of_house::Appetizer::Salad;
// Order breakfast
let mut meal = back_of_house::Breakfast::summer("Rye");
meal.toast = String::from("Wheat");
println!("I'd like {} toast please", meal.toast);
// This won't work because seasonal_fruit is private
// meal.seasonal_fruit = String::from("blueberries");
}Nested Paths
Bringing Multiple Items with Same Prefix
use std::collections::{HashMap, HashSet, BTreeMap};
fn main() {
let mut map = HashMap::new();
let mut set = HashSet::new();
let mut btree = BTreeMap::new();
}The Glob Operator
Bringing All Public Items
use std::collections::*;
fn main() {
let mut map = HashMap::new();
let mut set = HashSet::new();
}Separating Modules into Different Files
Project Structure Example
src/
āāā main.rs
āāā lib.rs
āāā front_of_house.rs
āāā front_of_house/
āāā hosting.rs
āāā serving.rsIn src/lib.rs:
mod front_of_house;
pub use front_of_house::hosting;
fn main() {
hosting::add_to_waitlist();
}In src/front_of_house.rs:
pub mod hosting;
pub mod serving;In src/front_of_house/hosting.rs:
pub fn add_to_waitlist() {
// Implementation
}Practical Examples
Example 1: Math Library Module
Create src/math.rs:
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
pub fn subtract(a: i32, b: i32) -> i32 {
a - b
}
pub fn multiply(a: i32, b: i32) -> i32 {
a * b
}
pub fn divide(a: f64, b: f64) -> Option<f64> {
if b != 0.0 {
Some(a / b)
} else {
None
}
}In src/main.rs:
mod math;
fn main() {
println!("5 + 3 = {}", math::add(5, 3));
println!("5 - 3 = {}", math::subtract(5, 3));
println!("5 * 3 = {}", math::multiply(5, 3));
match math::divide(10.0, 2.0) {
Some(result) => println!("10 / 2 = {}", result),
None => println!("Cannot divide by zero"),
}
}Example 2: Restaurant Management System
Create src/restaurant.rs:
pub mod menu {
pub struct MenuItem {
pub name: String,
pub price: f64,
}
impl MenuItem {
pub fn new(name: &str, price: f64) -> MenuItem {
MenuItem {
name: name.to_string(),
price,
}
}
}
pub fn display_menu(items: &[MenuItem]) {
println!("--- MENU ---");
for item in items {
println!("{} - ${:.2}", item.name, item.price);
}
}
}
pub mod order {
use super::menu::MenuItem;
pub struct Order {
pub items: Vec<MenuItem>,
pub table_number: u32,
}
impl Order {
pub fn new(table_number: u32) -> Order {
Order {
items: Vec::new(),
table_number,
}
}
pub fn add_item(&mut self, item: MenuItem) {
self.items.push(item);
}
pub fn total(&self) -> f64 {
self.items.iter().map(|item| item.price).sum()
}
}
}In src/main.rs:
mod restaurant;
use restaurant::menu::{MenuItem, display_menu};
use restaurant::order::Order;
fn main() {
let menu_items = vec![
MenuItem::new("Burger", 8.99),
MenuItem::new("Fries", 3.99),
MenuItem::new("Soda", 1.99),
];
display_menu(&menu_items);
let mut order = Order::new(5);
order.add_item(MenuItem::new("Burger", 8.99));
order.add_item(MenuItem::new("Soda", 1.99));
println!("Table {}: Total = ${:.2}", order.table_number, order.total());
}Workspaces
Managing Multiple Crates
Create Cargo.toml at root:
[workspace]
members = [
"adder",
"add-one",
]In adder/Cargo.toml:
[package]
name = "adder"
version = "0.1.0"
[dependencies]
add-one = { path = "../add-one" }In add-one/Cargo.toml:
[package]
name = "add-one"
version = "0.1.0"
[dependencies]Key Takeaways
- ā Modules organize code into logical groups
- ā
pubmakes items public; otherwise they're private - ā
Use
useto bring paths into scope - ā
External crates are added to
Cargo.toml - ā Modules can be separated into different files
- ā Workspaces manage multiple related crates
- ā Follow Rust naming conventions (snake_case for modules)
Ready for Chapter 11? ā Generics