cargo — The Build Tool
cargo is Rust's official build system, package manager, test runner, and documentation generator. Installed automatically alongside rustc by rustup, Cargo standardizes how every Rust project is structured, built, tested, and shared across macOS, Linux, and Windows.
What cargo Manages
In languages without a unified build tool, developers must combine separate tools for project configuration, compiler invocation, dependency downloading, testing, and documentation. Cargo handles all of these workflows through a single CLI:
+-------------------+
| cargo |
+---------+---------+
|
+--------------+--------------+--------------+--------------+
| | | | |
v v v v v
crates.io rustc rustdoc Test Harness rustfmt /
(Downloads & (Compiles (Builds HTML (Runs unit, clippy
resolves crates in docs for integration, (Formats &
dependencies) dependency project & and doc lints source
order) dependencies) tests) code)
Core cargo Commands Reference
| Command | Purpose |
|---|---|
cargo new <name> |
Creates a new directory <name> with Cargo.toml, src/main.rs, and a .git repository |
cargo new --lib <name> |
Creates a new library package with src/lib.rs instead of src/main.rs |
cargo init |
Initializes a Cargo project inside an existing directory |
cargo check |
Type-checks and borrow-checks the project rapidly without generating final machine code |
cargo build |
Compiles the project and its dependencies in debug mode (target/debug/) |
cargo build --release |
Compiles the project with full optimizations enabled (target/release/) |
cargo run |
Compiles (if needed) and runs the project's binary executable |
cargo test |
Compiles and executes unit tests, integration tests, and documentation tests |
cargo doc --open |
Generates HTML documentation for the project and all dependencies and opens it in a browser |
cargo add <crate> |
Adds a dependency from crates.io to Cargo.toml |
cargo remove <crate> |
Removes a dependency from Cargo.toml |
cargo update |
Updates dependencies in Cargo.lock to the newest compatible semver versions |
cargo clean |
Deletes the target/ directory and all compiled build artifacts |
cargo fmt |
Formats all Rust source files in the project using rustfmt |
cargo clippy |
Analyzes the project with the Clippy linter for common mistakes and unidiomatic code |
cargo install <crate> |
Builds and installs a binary crate from crates.io into ~/.cargo/bin |
Check your installed Cargo version at any time with:
cargo --version
cargo 1.85.0 (d73d2caf9 2024-12-31)
Project Files: Cargo.toml and Cargo.lock
Every Cargo package is driven by two files at its root directory:
my_project/
├── Cargo.toml <-- Package manifest (edited by you and `cargo add`)
├── Cargo.lock <-- Exact resolved dependency versions (generated by Cargo)
├── src/
│ └── main.rs <-- Crate entry point
└── target/ <-- Compiled artifacts and intermediate cache (ignored by Git)
Cargo.toml (The Manifest)
Written in TOML (Tom's Obvious Minimal Language), Cargo.toml declares the package's metadata, target Rust edition, and requested dependency version ranges:
[package]
name = "my_project"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
rand = "0.9"
| Section | Purpose |
|---|---|
[package] |
Defines name, version (Semantic Versioning MAJOR.MINOR.PATCH), edition, license, and rust-version |
[dependencies] |
Libraries required to compile and run the package |
[dev-dependencies] |
Libraries used only for tests, examples, and benchmarks (not included in production builds) |
[build-dependencies] |
Libraries used by a custom build.rs script during compilation |
[profile.dev] / [profile.release] |
Customizes compiler settings such as opt-level, debug, lto, and panic |
[workspace] |
Groups multiple related packages together so they share a single Cargo.lock and target/ directory |
Cargo.lock (The Lockfile)
While Cargo.toml specifies flexible Semantic Versioning ranges (for example, "1.0" means >= 1.0.0, < 2.0.0), Cargo.lock records the exact version and cryptographic SHA-256 checksum of every direct and transitive dependency selected during the first build.
- When someone else clones the project and runs
cargo build, Cargo usesCargo.lockto download the exact same dependency versions, guaranteeing reproducible builds across machines and CI pipelines. - Cargo never upgrades versions recorded in
Cargo.lockautomatically during normal builds; versions only change when you editCargo.tomlor explicitly runcargo update. - Starting with Cargo 1.73+,
cargo newdoes not ignoreCargo.lockin.gitignorefor either binary or library projects, making reproducible builds the default.
How Cargo Orchestrates rustc
When you run cargo build or cargo run, Cargo performs a multi-step build pipeline behind the scenes:
- Dependency Resolution: Reads
Cargo.tomlandCargo.lock, downloading any missing crate archives (.cratefiles) fromcrates.iointo~/.cargo/registry/. - Dependency Graph Compilation: Builds a directed acyclic graph (DAG) of crates and invokes
rustcin parallel for crates that do not depend on each other. - Incremental Fingerprinting: Hashes source files, compiler flags, and environment variables in
target/debug/.fingerprint/. On subsequent builds, Cargo only recompiles crates whose source or configuration changed. - Final Linking: Invokes
rustcon your root crate (src/main.rsorsrc/lib.rs), passing--externflags for every compiled dependency and placing the output binary intarget/debug/<name>.
To see the exact rustc commands and flags that Cargo executes under the hood, pass the --verbose (-v) flag:
cargo build --verbose
Compiling my_project v0.1.0 (/Users/username/my_project)
Running `rustc --crate-name my_project --edition=2024 src/main.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --crate-type bin --emit=dep-info,link -C embed-bitcode=no -C debuginfo=2 -C split-debuginfo=unpacked --check-cfg 'cfg(docsrs,test)' --check-cfg 'cfg(feature, values())' -C metadata=8a2f4b1c9e0d3a12 -C extra-filename=-8e1a2b3c4d5e6f70 --out-dir /Users/username/my_project/target/debug/deps -C incremental=/Users/username/my_project/target/debug/incremental -L dependency=/Users/username/my_project/target/debug/deps`
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.18s
Passing Arguments to Your Program with --
When running a program via cargo run or cargo test, flags written before -- are interpreted by cargo itself, whereas arguments written after -- are forwarded directly to your compiled binary:
# `--release` goes to Cargo; `--port 8080 --verbose` goes to your program's `std::env::args()`
cargo run --release -- --port 8080 --verbose
Extending Cargo with Custom Subcommands
Any executable on your PATH named cargo-<name> automatically becomes callable as cargo <name>. Official tools such as rustfmt (cargo-fmt) and clippy (cargo-clippy), as well as community tools installed via cargo install (such as cargo-expand, cargo-nextest, or cargo-watch), integrate into Cargo through this mechanism.
List all subcommands currently available in your environment with:
cargo --list