Modules and Packages
Modules and Packages in Rust
Rust's module system helps organize code into logical units for better maintainability and reusability.
Module Basics
Defining Modules
// In src/main.rs or src/lib.rs
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() {}
}
}
Public vs Private
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
// Private by default
fn seat_at_table() {}
}
}
pub fn eat_at_restaurant() {
// Absolute path
crate::front_of_house::hosting::add_to_waitlist();
// Relative path
front_of_house::hosting::add_to_waitlist();
// This won't work - seat_at_table is private
// front_of_house::hosting::seat_at_table();
}
File Structure
Single File Modules
// src/lib.rs
mod garden; // Looks for src/garden.rs
// src/garden.rs
pub fn plant_vegetables() {
println!("Planting vegetables");
}
Module Directories
// src/lib.rs
mod garden; // Looks for src/garden/mod.rs or src/garden.rs
// src/garden/mod.rs
pub mod vegetables;
pub mod fruits;
// src/garden/vegetables.rs
pub fn plant() {
println!("Planting vegetables");
}
// src/garden/fruits.rs
pub fn plant() {
println!("Planting fruits");
}
Modern Module Structure (Rust 2018+)
// src/lib.rs
mod garden; // Can look for src/garden.rs
// src/garden.rs
pub mod vegetables; // Looks for src/garden/vegetables.rs
pub mod fruits; // Looks for src/garden/fruits.rs
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;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
Creating Idiomatic use
Paths
// For functions: bring parent module into scope
use std::collections::HashMap;
let mut map = HashMap::new();
// For structs, enums, etc.: bring the item itself
use std::io::Result;
fn read_file() -> Result<String> {
// ...
}
Multiple Items
// Nested paths
use std::{cmp::Ordering, io};
// Multiple items from same module
use std::io::{self, Write};
// Glob operator (use sparingly)
use std::collections::*;
Re-exporting with pub use
// src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
// Re-export to make available at crate root
pub use crate::front_of_house::hosting;
// Users can now do:
// use restaurant::hosting;
Package Structure
Binary and Library Crates
my_package/
├── Cargo.toml
├── src/
│ ├── main.rs # Binary crate root
│ ├── lib.rs # Library crate root
│ └── bin/
│ ├── tool1.rs # Additional binary
│ └── tool2.rs # Additional binary
Complex Project Structure
my_project/
├── Cargo.toml
├── src/
│ ├── lib.rs
│ ├── config/
│ │ ├── mod.rs
│ │ ├── parser.rs
│ │ └── validator.rs
│ ├── network/
│ │ ├── mod.rs
│ │ ├── client.rs
│ │ └── server.rs
│ └── utils/
│ ├── mod.rs
│ └── helpers.rs
Visibility and Privacy
Super and Self
fn function() {
println!("called function()");
}
mod cool {
pub fn function() {
println!("called cool::function()");
}
pub mod nested {
pub fn function() {
println!("called cool::nested::function()");
// Call parent module function
super::function();
// Call crate function
crate::function();
}
}
}
Struct Field Visibility
mod back_of_house {
pub struct Breakfast {
pub toast: String,
seasonal_fruit: String, // Private field
}
impl Breakfast {
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peaches"),
}
}
}
}
pub fn eat_at_restaurant() {
let mut meal = back_of_house::Breakfast::summer("Rye");
meal.toast = String::from("Wheat");
// This won't work - seasonal_fruit is private
// meal.seasonal_fruit = String::from("blueberries");
}
Practical Examples
API Module Organization
// src/lib.rs
pub mod api {
pub mod v1 {
pub mod users;
pub mod posts;
}
pub mod v2 {
pub mod users;
pub mod posts;
pub mod comments;
}
}
// src/api/v1/users.rs
use crate::models::User;
pub fn get_user(id: u64) -> Option<User> {
// Implementation
}
pub fn create_user(data: UserData) -> Result<User, Error> {
// Implementation
}
Internal Module Pattern
// src/database/mod.rs
mod connection;
mod query_builder;
// Re-export public interface
pub use connection::Connection;
pub use query_builder::QueryBuilder;
// Keep implementation details private
use connection::ConnectionPool;
use query_builder::internal_helper;
Test Module Pattern
// src/lib.rs
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 2), 4);
}
mod integration {
use super::*;
#[test]
fn test_complex_scenario() {
// Complex test
}
}
}
Plugin Architecture
// src/plugins/mod.rs
pub trait Plugin {
fn name(&self) -> &str;
fn execute(&self);
}
pub mod builtin;
pub mod custom;
// src/plugins/builtin/mod.rs
mod logger;
mod metrics;
pub use logger::LoggerPlugin;
pub use metrics::MetricsPlugin;
// src/main.rs
use my_app::plugins::{Plugin, builtin::LoggerPlugin};
fn main() {
let plugin = LoggerPlugin::new();
plugin.execute();
}
Module Best Practices
Clear Module Hierarchy
// Good: Clear, logical organization
crate
├── models
│ ├── user
│ ├── post
│ └── comment
├── controllers
│ ├── user_controller
│ └── post_controller
├── services
│ ├── auth_service
│ └── email_service
└── utils
├── validators
└── formatters
Module Documentation
//! # My Library
//!
//! `my_library` provides utilities for working with data.
/// The authentication module handles user authentication
pub mod auth {
//! Authentication and authorization utilities
/// Authenticates a user with credentials
pub fn authenticate(username: &str, password: &str) -> bool {
// Implementation
}
}
Common Patterns
Prelude Module
// src/prelude.rs
pub use crate::error::{Error, Result};
pub use crate::config::Config;
pub use crate::types::*;
// Users can import everything commonly needed:
// use my_crate::prelude::*;
Re-export Pattern
// src/lib.rs
mod implementation {
pub struct InternalThing;
pub struct PublicThing;
}
// Selective re-export
pub use implementation::PublicThing;
// InternalThing remains private
Tips and Guidelines
- Keep modules focused on a single responsibility
- Use clear, descriptive names for modules
- Minimize public exports - only expose what's necessary
- Group related functionality together
- Use
mod.rs
sparingly in modern Rust (prefer named files) - Document module purpose with module-level docs
- Create a prelude for commonly used items
- Use
pub(crate)
for crate-internal visibility - Organize tests near the code they test
- Consider the user's perspective when designing module structure