19. The Module System, Cargo, crates.io
Once a Rust project grows you split code into modules and pull in external crates. This lesson covers the `mod` keyword and the file-split convention, `pub` visibility, `use` paths, declaring dependencies in Cargo.toml, and how to find and add packages from crates.io.
What you'll learn
- 1Split code into modules with `mod`
- 2Control visibility with `pub`
- 3Shorten paths with `use`
- 4Add external crates in Cargo.toml's [dependencies]
- 5Use cargo add / cargo search / cargo doc for package workflow
Overview
main.rs scaling past 100 lines turns into a mess. Rust's one-file-equals-one-module convention makes splitting natural without ceremony.
Core Concepts
1) crate vs. module
- **crate** β one Cargo.toml = one package = one build unit
- **module** β namespace unit inside a crate
- Binary crate root = `src/main.rs`, library crate root = `src/lib.rs`
2) mod and file layout
// src/main.rs
mod math; // looks for math.rs or math/mod.rs
fn main() {
println!("{}", math::add(2, 3));
}
// src/math.rs
pub fn add(a: i32, b: i32) -> i32 { a + b }3) pub β visibility
- Default = **private** (same-module only)
- `pub` β exported
- `pub(crate)` β exported within the crate
- `pub(super)` β exported to parent module
4) use β path shortening
use std::collections::HashMap;
use std::io::{self, Read}; // self = io itself, Read = trait
use crate::math::add as plus; // alias5) Cargo.toml dependencies
[dependencies]
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
thiserror = "1"Hands-on Examples
Sample directory layout:
src/
βββ main.rs
βββ config.rs
βββ api/
βββ mod.rs (or src/api.rs)
βββ user.rs
βββ post.rs// src/main.rs
mod config;
mod api;
fn main() {
let cfg = config::load();
api::user::create(&cfg);
}
// src/api/mod.rs (or src/api.rs + src/api/ folder)
pub mod user;
pub mod post;
// src/api/user.rs
use crate::config::Config;
pub fn create(cfg: &Config) { /* ... */ }Add an external crate and use it:
cargo add serde --features derive
cargo add chrono
cargo search regex # search crates.iouse serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
struct User { name: String, age: u32 }
fn main() {
let u = User { name: "Alice".into(), age: 30 };
let json = serde_json::to_string(&u).unwrap();
println!("{}", json);
}Common Mistakes
Q. mod vs. use β what's the difference?
A. **mod** brings a module into existence, **use** shortens a path to an existing module. Without `mod foo;` you can't `use crate::foo`.
Q. Without `pub`, can I use it from the same file?
A. Yes β within the same module (= same file or same `mod` block) you can access private items. **External modules need pub.**
Q. Should Cargo.lock be committed?
A. **Binary crates yes, library crates no.** Binaries want reproducible builds; libraries have no inherent lockfile because users have their own.
Recap
- crate = build unit, module = namespace unit
- mod declares, file/folder convention auto-maps
- pub / pub(crate) / pub(super) for visibility tiers
- use shortens paths, Cargo.toml manages deps, cargo add for quick install
Try It Yourself
- Split main.rs and math.rs, call math::add from main
- Add src/api/ with user.rs / post.rs and use them from main
- `cargo add chrono` and print the current time
All lecture materials and example code are openly available on GitHub.
View on GitHub β