Modules and Crates in Rust
Modules help you organize code into logical units. They control visibility and prevent naming conflicts. A crate is a package of Rust code, and modules are how you structure it.
Think of modules as namespaces that group related functionality together.
mod math {
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
pub fn multiply(a: i32, b: i32) -> i32 {
a * b
}
}
fn main() {
println!("2 + 3 = {}", math::add(2, 3));
println!("2 * 3 = {}", math::multiply(2, 3));
}
Try it Yourself ->
Visibility with pub
By default, everything in a module is private. Use pub to make items visible to outside code. You can also use pub(crate) to limit visibility to within the crate.
mod restaurant {
pub struct Breakfast {
pub toast: String,
seasonal_fruit: String,
}
impl Breakfast {
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peaches"),
}
}
}
}
fn main() {
let mut meal = restaurant::Breakfast::summer("wheat");
meal.toast = String::from("rye".to_string());
println!("I'd like {} toast please", meal.toast);
// Can't access meal.seasonal_fruit because it's private
}
Try it Yourself ->
Using use for Imports
The use keyword brings items into scope, so you don't have to write the full path every time. It's like importing in other languages.
mod shapes {
pub fn circle_area(radius: f64) -> f64 {
std::f64::consts::PI * radius * radius
}
pub fn rectangle_area(width: f64, height: f64) -> f64 {
width * height
}
}
use shapes::{circle_area, rectangle_area};
fn main() {
println!("Circle area: {:.2}", circle_area(5.0));
println!("Rectangle area: {:.2}", rectangle_area(4.0, 6.0));
}
Try it Yourself ->
Crate Root and Module Tree
Every crate has a root file (main.rs or lib.rs) that defines the top-level module. The module tree is like a directory structure for your code.
// In main.rs, you can define modules inline:
mod config {
pub const MAX_SIZE: usize = 100;
pub const VERSION: &str = "1.0";
}
mod database {
pub fn connect(url: &str) -> bool {
println!("Connecting to {}", url);
true
}
}
use config::{MAX_SIZE, VERSION};
fn main() {
println!("Version: {}", VERSION);
println!("Max size: {}", MAX_SIZE);
database::connect("localhost:5432");
}
Try it Yourself ->
Cargo.toml Dependencies
Cargo.toml is where you declare dependencies. The crates.io ecosystem has thousands of packages ready to use.
// Example Cargo.toml section:
// [dependencies]
// serde = { version = "1.0", features = ["derive"] }
// tokio = { version = "1", features = ["full"] }
// reqwest = "0.11"
fn main() {
println!("Dependencies are declared in Cargo.toml");
println!("Use 'cargo add' to add new dependencies");
}
Try it Yourself ->
File Organization
Rust has two ways to organize modules in files. You can use mod.rs files or the folder naming convention. Both approaches are valid and widely used.
The folder naming convention (src/module_name.rs) is generally preferred for new projects because it's easier to navigate.
Crates.io Ecosystem
Crates.io is Rust's package registry. It has packages for everything from web frameworks to cryptography to game development. Before writing your own code, check if there's already a crate for what you need.
Popular crates like serde, tokio, and reqwest are well-maintained and widely used in production.