Rust 09. Crates, Packages, and Project Layout
Summary
Once you move past syntax basics in Rust, the next common point of confusion is project structure. If Cargo.toml, src/main.rs, src/lib.rs, mod, use, and pub are still blurred together, even a small project quickly becomes hard to organize.
This post focuses on the relationship between packages and crates, the difference between a binary crate and a library crate, and how main.rs, lib.rs, mod, use, and pub fit together. The practical beginner rule is simple: treat main.rs as the entry point, move reusable logic into lib.rs, and expose only the items that really need to be public.
Document Information
- Written on: 2026-04-15
- Verification date: 2026-04-16
- Document type: tutorial
- Test environment: Windows 11 Pro, Windows PowerShell, Cargo CLI examples
- Test version: rustc 1.94.0, cargo 1.94.0
- Source grade: only official documentation is used.
- Note: this post stays focused on the basic structure of a single Cargo package and intentionally leaves out broader topics such as workspaces and features.
Problem Definition
After learning basic module syntax, beginners often get stuck at the next step.
- It is not obvious whether
packageandcratemean the same thing. - It is hard to tell when
src/main.rsandsrc/lib.rsshould exist together. - Once code is split into multiple files, the roles of
mod,use, andpubcan feel vague.
This post stays intentionally narrow. It explains how to read and split one Cargo project, without going into wider topics like workspaces, publishing, features, or path dependencies.
Verified Facts
- According to the official Rust Book, a Cargo package is the unit described by
Cargo.toml, and a package can contain at most one library crate and any number of binary crates. Evidence: Packages and Crates - According to the official docs, a binary crate has a
mainfunction as its entry point, while a library crate exposes reusable functionality. Evidence: Packages and Crates - According to the official docs,
moddefines modules,usebrings a path into the current scope, andpubcontrols visibility. Evidence: Defining Modules to Control Scope and Privacy, Paths for Referring to an Item in the Module Tree, Bringing Paths into Scope with the use Keyword - According to the official docs, modules can be moved into separate files and connected from the crate root. Evidence: Separating Modules into Different Files
A useful beginner mental model is this layout:
rust-layout-demo/
Cargo.toml
src/
main.rs
lib.rs
math.rs
It helps to read that structure in this order:
Cargo.toml: check the package name and dependencies.src/main.rs: check where execution starts.src/lib.rs: check which reusable modules the crate exposes.src/math.rs: check where the actual feature logic lives.
For example, lib.rs can expose a module like this:
pub mod math;
Then math.rs can hold the reusable functions.
pub fn add(left: i32, right: i32) -> i32 {
left + right
}
pub fn subtract(left: i32, right: i32) -> i32 {
left - right
}
And main.rs can stay focused on wiring execution together.
use rust_layout_demo::math;
fn main() {
let sum = math::add(10, 20);
let diff = math::subtract(20, 5);
println!("sum = {}", sum);
println!("diff = {}", diff);
}
The important beginner takeaway is that execution starts in main.rs, but the real logic does not need to stay there. Once you move reusable code into lib.rs and its modules, later topics like testing, file I/O, CLI tools, and small projects become much easier to structure.
Directly Confirmed Results
- Directly confirmed result: the Rust toolchain versions available in the current writing environment were:
rustc --version
cargo --version
- Observed output:
rustc 1.94.0 (4a4ef493e 2026-03-02)
cargo 1.94.0 (85eff7c80 2026-01-15)
- Directly confirmed result: when I ran the
main.rsexample in a temporary Cargo project with the same structure as the post, the output was:
cargo run
- Observed output:
sum = 30
diff = 15
- Limitation of direct reproduction: I reproduced the representative example in a temporary Cargo project, but I did not add a separate example project to this repository.
Interpretation / Opinion
- My interpretation is that the most important early distinction is this: a package is the Cargo-managed bundle, while a crate is the compilation unit.
- Opinion: once a function looks reusable, moving it out of
main.rsand into a library module usually makes testing and future refactoring easier. - Opinion:
pubshould be treated as an API boundary, not as a convenience switch. Keeping most items private by default tends to produce cleaner project structure.
Limits and Exceptions
- This post explains a single-package project and does not cover Cargo workspaces.
- It does not go into finer visibility details such as
pub(crate),super, or deeper nested module layouts. - You can build a Rust program without a library crate. Still, once the project grows, the
lib.rssplit often becomes easier to maintain. - Package names with hyphens introduce additional crate-name details in code, but this post keeps the example intentionally simple.
댓글남기기