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.rs

In 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
  • āœ… pub makes items public; otherwise they're private
  • āœ… Use use to 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

šŸ¦€ Rust Programming Tutorial

Learn from Zero to Advanced

Built with Next.js and Tailwind CSS • Open Source