Once you move past part 10, projects balloon quickly. When every function and struct lives in a single file, tracking ownership rules becomes painful. Rust's module and package system is designed to carve large codebases into digestible pieces, so let's practice that split.
Big Picture: crate, package, module
- package: The top-level unit anchored by
Cargo.toml. It can contain one or more crates. Most beginner projects ship with just one binary crate. - crate: What the compiler builds independently.
src/main.rsstarts a binary crate,src/lib.rsstarts a library crate. - module (
mod): A logical grouping inside a crate. Declare it inline with amod {}block or pull it out into files.
A mental picture: a package holds one or more crates, and each crate nests many modules.
Declaring Modules Inline
Start small by placing mod blocks inside src/main.rs.
mod auth {
pub fn login(user: &str) {
println!("{} logged in", user);
}
}
fn main() {
auth::login("yuna");
}
mod auth { ... }defines a module namedauth.- Functions intended for use outside the module must be
pub. - Use
::to walk the module path.
Splitting Modules into Files
As files grow, declare mod foo; and Rust will look for foo.rs or foo/mod.rs.
src/
├── main.rs
├── auth.rs
└── user/
├── mod.rs
└── profile.rs
main.rs pulls modules in like this:
mod auth;
mod user;
fn main() {
auth::login("yuna");
user::profile::show("yuna");
}
Then user/mod.rs wires up its children:
pub mod profile;
pub fn list() {
println!("User list");
}
- Directory-style modules historically use
mod.rs, though newer editions allowuser.rsplususer/profile.rsas an alternative layout. pub mod profile;both includesprofile.rsand re-exports it so outside modules can reach it.
In other words, when user/mod.rs contains pub mod profile;, Rust expects a sibling file user/profile.rs.
Shortening Paths with use
Avoid repeating long paths by importing them.
use crate::auth::login;
use crate::user::profile::show;
fn main() {
login("yuna");
show("yuna");
}
crate::points to the crate root.super::climbs one module up,self::refers to the current module.- Use braces like
use crate::user::{profile, list};to import multiple items from the same path.
Remember use only shortens names; it does not re-export them. Use pub use to expose imports further up the tree.
Designing Visibility
Rust's module system shines when you deliberately control what becomes public.
pub(crate)exposes items only to the current crate.pub(super)exposes to the parent module.- Keep implementation details (database adapters, low-level helpers) private and expose only the API surface with
pub.
Quick Tour of Cargo Workspaces
Large projects often stitch multiple crates together with a workspace. Here's just the taste.
[workspace]
- Declare the workspace in the root
Cargo.toml, while every member crate keeps its own manifest. - Run
cargo build -p appto build a specific crate, keeping module boundaries crisp.
Practice in CodeSandbox
The sandbox below uses CodeSandbox's Rust starter. Move the main code into src/main.rs, then compare cargo check and cargo run so you can read the compiler feedback beside the final output.
💬 댓글
이 글에 대한 의견을 남겨주세요