Rust Development Environment

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 uses Cargo.lock to download the exact same dependency versions, guaranteeing reproducible builds across machines and CI pipelines.
  • Cargo never upgrades versions recorded in Cargo.lock automatically during normal builds; versions only change when you edit Cargo.toml or explicitly run cargo update.
  • Starting with Cargo 1.73+, cargo new does not ignore Cargo.lock in .gitignore for 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:

  1. Dependency Resolution: Reads Cargo.toml and Cargo.lock, downloading any missing crate archives (.crate files) from crates.io into ~/.cargo/registry/.
  2. Dependency Graph Compilation: Builds a directed acyclic graph (DAG) of crates and invokes rustc in parallel for crates that do not depend on each other.
  3. 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.
  4. Final Linking: Invokes rustc on your root crate (src/main.rs or src/lib.rs), passing --extern flags for every compiled dependency and placing the output binary in target/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