Packages and crates¶
Level: 101 · for newcomers
One line: A package is a folder with a Cargo.toml; a crate is one unit rustc compiles — a library or a binary — and a package holds at most one library crate and any number of binary crates, each with its own module tree rooted at one file. Every binary sees the library by the package's name and the other binaries not at all.
| Word | Lives in | Named by |
|---|---|---|
| package | Cargo.toml and the folder around it |
name = "…" in the manifest |
| library crate | src/lib.rs, the crate root |
the package's name, - turned into _ |
| binary crate | src/main.rs, or one file each under src/bin/ |
src/main.rs takes the package's name; a file under src/bin/ takes its own |
| module | mod name; or mod name { … } inside a crate |
the mod line — see Modules and visibility |
One package with all three kinds of target:
[package]
name = "pg-pkg-two-crates"
version = "0.1.0"
edition = "2024"
publish = false
//! The library crate. Its name is the package's, with `-` turned into `_`,
//! because `pg-pkg-two-crates` is not a Rust identifier and `pg_pkg_two_crates` is.
mod greeting {
pub fn hello(from: &str) -> String {
format!("hello from {from}, through the library crate {}", module_path!().split("::").next().unwrap_or("?"))
}
}
pub use greeting::hello;
//! The binary crate that shares the package's name. It sees the library by
//! that name, not by `crate::`, which here means main.rs itself.
use pg_pkg_two_crates::hello;
fn main() {
println!("{}", hello("src/main.rs"));
println!("this crate is called {}", module_path!());
}
//! A second binary crate, from src/bin/. Same library, same spelling.
use pg_pkg_two_crates::hello;
fn main() {
println!("{}", hello("src/bin/report.rs"));
println!("this crate is called {}", module_path!());
}
Verified output of cargo run -q -p pg-pkg-two-crates --bin pg-pkg-two-crates — declared in cargo_runs.toml and regenerated by tools/run_cargo_demos.py, never hand-typed.
hello from src/main.rs, through the library crate pg_pkg_two_crates
this crate is called pg_pkg_two_crates
Verified output of cargo run -q -p pg-pkg-two-crates --bin report — declared in cargo_runs.toml and regenerated by tools/run_cargo_demos.py, never hand-typed.
hello from src/bin/report.rs, through the library crate pg_pkg_two_crates
this crate is called report
cargo metadata is how Cargo itself lists what it will build. Asked for the committed package, then for a fresh cargo new, it answers the questions this page exists for:
Verified output of sh targets.sh — declared in cargo_runs.toml and regenerated by tools/run_cargo_demos.py, never hand-typed.
1. The committed package pg-pkg-two-crates: kind, target name, file
lib pg_pkg_two_crates src/lib.rs
bin pg-pkg-two-crates src/main.rs
bin report src/bin/report.rs
2. cargo new my_package, on this toolchain
edition = "2024"
bin my_package src/main.rs
3. Listing 6.2: bin/my_binary.rs at the package root, beside src/
bin my_package src/main.rs
Hello, world!
4. The same file moved to src/bin/
bin my_binary src/bin/my_binary.rs
bin my_package src/main.rs
5. rustc alone: one file, one crate, named after the file
main
my_binary
The library's name has an underscore¶
The package is pg-pkg-two-crates. The library crate is pg_pkg_two_crates, because a crate name is a Rust identifier and - is not allowed in one, so use pg_pkg_two_crates::hello; is what main.rs and report.rs write. Section 1 of the key shows both spellings side by side: the lib target is named with the underscore, the bin target that main.rs makes keeps the dash, since an executable's file name can hold one.
Paths inside a crate, and the name from outside¶
Inside a crate every path starts from one of three places: crate:: for the root, self:: for the current module, super:: for its parent. main.rs and lib.rs are two crates, so crate:: in main.rs is main.rs — the library is reached by its name, pg_pkg_two_crates::hello, the way any dependency is. The kata at the end of this page walks five places in one package and says where crate:: points from each; One module per file has the same fact from the module side.
What rustc file.rs makes¶
One crate and no package. rustc --print crate-name on src/main.rs answers main, and on src/bin/my_binary.rs it answers my_binary — section 5 of the key — because without Cargo the crate is named after the file, and CARGO_PKG_NAME is not set at all. That is how every example in this library is compiled, and Running a scratch program is the page for it.
The trap: shared code in main.rs¶
Each file under src/bin/ is a crate of its own, so a module declared in main.rs does not exist for it:
//! Shared code put in main.rs, where the other binary cannot see it.
mod shared {
pub fn greeting() -> &'static str {
"hello"
}
}
fn main() {
println!("{}", shared::greeting());
}
//! `crate::` here is other.rs, a crate with no `shared` module in it.
fn main() {
println!("{}", crate::shared::greeting());
}
Verified output of cargo build -q -p pg_pkg_bin_sees_no_main, which fails on purpose — declared in cargo_runs.toml and regenerated by tools/run_cargo_demos.py, never hand-typed.
error[E0433]: cannot find `shared` in `crate`
--> bin_sees_no_main/src/bin/other.rs:4:27
|
4 | println!("{}", crate::shared::greeting());
| ^^^^^^ could not find `shared` in the crate root
For more information about this error, try `rustc --explain E0433`.
error: could not compile `pg_pkg_bin_sees_no_main` (bin "other") due to 1 previous error
crate in other.rs is other.rs. Naming the package instead, use pg_pkg_bin_sees_no_main::shared;, fails the same way, since there is no library crate for that name to mean — rustc even suggests cargo add-ing your own package. The fix is src/lib.rs: put shared there, and main.rs and other.rs both use it by the package's name.
The book's bin/ folder, and what cargo new writes today¶
Rust: The Practical Guide (Nouman Azam, Rheinwerk 2025) draws its package as Cargo.toml, src/main.rs and a bin/my_binary.rs beside src/, and its manifest says edition = "2021". Sections 2 to 4 of the key measure both on this toolchain. cargo new on rustc 1.98.0 writes edition = "2024". A bin/ folder at the package root is not a target: with the book's tree, cargo metadata lists the one binary main.rs makes, the file in bin/ is never compiled, and cargo run prints Hello, world! as if it were not there. Moved to src/bin/, the file becomes a second binary target, and from then on cargo run needs --bin or a default-run line. The rest of that chapter is checked on What Rust: The Practical Guide says about packages, crates and modules, run.
What crates.io calls a crate¶
crates.io publishes packages and calls them crates, and the docs.rs page for serde is one crate's documentation because that package has exactly one library. The loose usage is harmless until a package has a library and two binaries, as this page's does: then "the crate" is three things, cargo build makes all three, and cargo run asks which one you meant. Publishing a crate is where the word is used in the loose sense on purpose.
Where this sits¶
Modules and visibility is the tree inside one crate. One module per file is how that tree maps onto files. Workspaces are the level above the package, several of them sharing one Cargo.lock and one target/ — which is what this page's demo/ folder is.
Practice¶
Where does crate:: point? For each of five places in one package — src/lib.rs, src/main.rs, src/bin/tool.rs, a #[cfg(test)] mod tests inside lib.rs, and tests/it.rs — write down what crate:: refers to, whether use my_package::X is available, and whether private items of lib.rs can be seen. Then say what the crate is called in each case.
Check the last question by printing module_path!() from the crate root and from two nested modules of one file compiled with bare rustc, and explain why src/main.rs is the one file whose crate name does not come from its own file name.
Solution
pkg_crate_kata.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.
//! Kata solution: where `crate::` points from five places in one package, and
//! what this crate is called. The measured half comes from `module_path!()`.
//!
//! rustc --edition 2024 pkg_crate_kata.rs -o /tmp/pck && /tmp/pck
mod product {
pub mod category {
pub fn path_here() -> &'static str {
module_path!()
}
}
pub fn path_here() -> &'static str {
module_path!()
}
}
fn main() {
println!("1. What this crate is called, measured");
println!(" module_path!() at the root = {}", module_path!());
println!(" module_path!() in product = {}", product::path_here());
println!(" module_path!() in product::category = {}", product::category::path_here());
println!(" Under bare rustc the crate is named after the file: pkg_crate_kata.rs");
println!(" gives pkg_crate_kata. Under Cargo a bin target is named after its file");
println!(" too (src/bin/report.rs -> report), except src/main.rs, which takes the");
println!(" package's name, and src/lib.rs, which takes it with `-` turned into `_`.");
println!();
println!("2. Where `crate::` points, from five places in one package");
println!(" src/lib.rs crate:: is lib.rs; `use my_package::X` is not");
println!(" available (a crate has no name for itself)");
println!(" src/main.rs crate:: is main.rs itself, a second crate;");
println!(" the library is `use my_package::X`");
println!(" src/bin/tool.rs crate:: is tool.rs, a third crate; it sees the");
println!(" library by name and main.rs not at all");
println!(" #[cfg(test)] mod tests inside lib.rs: crate:: is lib.rs, super:: is the");
println!(" enclosing module, private items included");
println!(" tests/it.rs its own crate, like a binary: `use my_package::X`,");
println!(" pub items only");
println!();
println!("3. The trap, from the page's demo");
println!(" shared code in src/main.rs and `crate::shared::greeting()` in src/bin/other.rs");
println!(" -> E0433: could not find `shared` in the crate root");
println!(" the fix is a src/lib.rs: every binary of the package can `use` it by name");
}
Verified output of pkg_crate_kata.rs — regenerated by tools/run_examples.py, never hand-typed.
1. What this crate is called, measured
module_path!() at the root = pkg_crate_kata
module_path!() in product = pkg_crate_kata::product
module_path!() in product::category = pkg_crate_kata::product::category
Under bare rustc the crate is named after the file: pkg_crate_kata.rs
gives pkg_crate_kata. Under Cargo a bin target is named after its file
too (src/bin/report.rs -> report), except src/main.rs, which takes the
package's name, and src/lib.rs, which takes it with `-` turned into `_`.
2. Where `crate::` points, from five places in one package
src/lib.rs crate:: is lib.rs; `use my_package::X` is not
available (a crate has no name for itself)
src/main.rs crate:: is main.rs itself, a second crate;
the library is `use my_package::X`
src/bin/tool.rs crate:: is tool.rs, a third crate; it sees the
library by name and main.rs not at all
#[cfg(test)] mod tests inside lib.rs: crate:: is lib.rs, super:: is the
enclosing module, private items included
tests/it.rs its own crate, like a binary: `use my_package::X`,
pub items only
3. The trap, from the page's demo
shared code in src/main.rs and `crate::shared::greeting()` in src/bin/other.rs
-> E0433: could not find `shared` in the crate root
the fix is a src/lib.rs: every binary of the package can `use` it by name
If you are coming from another language¶
- Python. A distribution (what
pipinstalls, named inpyproject.toml↗) contains import packages and modules; Rust's package → crate → module is the same three levels, with the crate as a compilation unit Python does not have. The underscore rule is Python's too: the distributionscikit-learnis imported assklearn, andtyping-extensionsastyping_extensions, because an import name has to be an identifier. - Go. A Go module (
go.mod) holds packages, and a package is one directory; Rust's module tree is inside the crate rather than one per directory, and a Gomainpackage is the nearest thing to a binary crate. - Java. A Maven artifact is the package, a JAR is roughly the crate, and Java packages are the module tree. A JAR can hold several
mainclasses and you pick one at run time; a Rust package builds one executable per binary crate and you pick at build time. - C and C++. No standard unit above the translation unit: a static library, a shared library or an executable is what a crate compiles into, and what a Makefile ↗ lists by hand is what
cargo metadatadiscovers from the file layout.
See also¶
- Modules and visibility — the tree inside a crate
- One module per file —
mod name;and the file next door - Bringing names in with
use—crate::and friends - What Rust: The Practical Guide says about packages, crates and modules, run — a book chapter's store, rebuilt as a package with a library and two binaries, and every error it stops at
- Running a scratch program — one crate, no package, and
src/bin/ - From one
.rsfile to a Cargo project —cargo initadopting a file, andcargo run -vshowing the crate it builds - Scaffolding a practice tree — several packages under one workspace root
- Workspaces — the level above packages
- Publishing a crate — what "crate" means on crates.io
- The Cargo Book: Cargo targets ↗ — target auto-discovery,
src/bin/,default-run
Po polsku¶
Pakiet (package) to katalog z Cargo.toml; skrzynia (crate) to jedna jednostka kompilacji — biblioteka albo program wykonywalny. Pakiet ma najwyżej jedną skrzynię biblioteczną (src/lib.rs) i dowolnie wiele binarnych (src/main.rs i po jednym pliku w src/bin/); każda ma własne drzewo modułów, dlatego plik w src/bin/ nie widzi modułów z main.rs (E0433, „could not find shared in the crate root”), a wspólny kod trafia do lib.rs, którego każdy program w pakiecie używa po nazwie pakietu. Nazwa biblioteki zamienia myślnik na podkreślenie, bo nazwa skrzyni musi być identyfikatorem: pakiet pg-pkg-two-crates to w kodzie pg_pkg_two_crates. cargo metadata wypisuje cele (targets), które Cargo naprawdę znajdzie — i pokazuje, że katalog bin/ obok src/ nie jest żadnym z nich; na rustc 1.98.0 cargo new wpisuje do manifestu edition = "2024".
Szukaj po polsku: pakiet a skrzynia · moduły w Ruście · rust package vs crate vs module · cargo src/bin shared code lib.rs · cargo metadata targets · rust crate name underscore dash