17.1 Packages: The Top-Level Unit
17.1.1 What Is a Package?
A package is a collection of Rust crates that provides a set of functionality. It can contain multiple binary crates and optionally one library crate. The structure of a package is defined by a Cargo.toml
file, which contains metadata about the package, such as its name, version, authors, and dependencies.
The Cargo command cargo new my_package
creates a new package containing one binary crate, with the following file structure:
$ cargo new my_package
Created binary (application) `my_package` package
$ tree my_package/
my_package/
├── Cargo.toml
└── src
└── main.rs
2 directories, 2 files
Alternatively, we can create a library package by specifying the --lib
flag:
$ cargo new my_rust_lib --lib
Created library `my_rust_lib` package
$ cd my_rust_lib/
$ tree
.
├── Cargo.toml
└── src
└── lib.rs
2 directories, 2 files
17.1.2 Components of a Package
A typical Rust package includes:
Cargo.toml
: The manifest file containing package metadata, dependencies, and build configuration.src/
: The source code directory, which includes the crate roots (main.rs
orlib.rs
) and optionally additional module files or folders.Cargo.lock
: A lockfile that records the exact versions of dependencies used, ensuring consistent builds.- Tests and Documentation: Optional directories like
tests/
,examples/
, anddocs/
for integration tests, example code, and additional documentation.
Example Cargo.toml
:
[package]
name = "my_package"
version = "0.1.0"
authors = ["Author Name <author@example.com>"]
edition = "2021"
[dependencies]
rand = "0.8"
When we build a binary package with the command cargo build
, a target
directory is created, which contains debug
and release
folders containing the executable file and other artifacts.
17.1.3 Workspaces: Managing Multiple Packages
For very large software projects that might contain multiple related packages developed closely together, workspaces can be used. Workspaces share a common Cargo.lock
and output directory (target/
), which simplifies dependency management and improves compilation times.
Example Workspace Layout:
my_workspace/
├── Cargo.toml
├── package_a/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
└── package_b/
├── Cargo.toml
└── src/
└── main.rs
Workspace-level Cargo.toml
:
[workspace]
members = ["package_a", "package_b"]
17.1.4 Packages with Multiple Binary Crates
A single package can contain additional binary crates, created by placing their Rust files in the src/bin/
directory. Each file corresponds to a separate binary crate that can be built and run independently.
Example Structure:
my_package/
├── Cargo.toml
└── src/
├── main.rs // Primary binary crate
└── bin/
├── tool.rs // Additional binary crate
└── helper.rs
You can build and run these binaries using Cargo commands:
- Build all binaries:
cargo build --bins
- Run a specific binary:
cargo run --bin tool
For more details, consult the Cargo Book.
17.1.5 Relationship Between Packages and Crates
In Rust:
- A crate is a compilation unit; the compiler processes each crate as a whole.
- A package is a collection of crates that are built and managed together.
A package can contain:
- One library crate (optional).
- Any number of binary crates (including none).
For a package with a single crate, the package and crate appear identical. However, understanding the distinction is important when working with more complex projects.