Cargo Basics
Cargo is Rust’s project tool: it creates packages, builds crates, runs binaries, runs tests, downloads dependencies, and records exact dependency versions for reproducible builds.
What it is
Cargo combines the jobs that many ecosystems split across several tools. It is the default build system, package manager, test runner, dependency downloader, documentation builder, and convention enforcer for Rust projects.
Small single-file programs can be compiled with rustc, but normal Rust work
starts with Cargo. The Book introduces Cargo immediately after “Hello, world!”
because dependencies, multiple files, release builds, tests, and project metadata
become easier when one tool owns the package model.
How it works
cargo new hello_cargo creates a package directory with Cargo.toml and
src/main.rs. Cargo.toml describes package metadata, edition, dependencies,
targets, and other configuration. Source code lives under src/; build outputs
go under target/ rather than beside your source files.
Cargo reads the manifest, resolves dependencies from the configured registry,
compiles dependency crates, compiles your crate, and stores build artifacts in
profile-specific directories such as target/debug and target/release.
The first dependency-resolving build creates Cargo.lock, which records exact
versions. You normally edit Cargo.toml Manifest, not the lockfile by hand.
Cargo’s model has a few names worth separating early. A package is the
directory described by one manifest. A package can contain one library crate,
zero or more binary crates, examples, tests, and benches. A crate is a
compilation unit. Cargo discovers many targets by conventional paths, then
invokes rustc with the right crate roots, dependency search paths, profile
settings, features, and edition.
On edition 2024 projects generated by current Cargo, the manifest explicitly
contains edition = "2024". That also means resolver version 3 is the default
for the package or workspace, so dependency resolution prefers versions
compatible with declared rust-version when possible.
Example
fn main() {
let package = "hello_cargo";
println!("Cargo can build and run the {package} package.");
}Worked example
The smallest binary package has a manifest and a crate root:
hello_cargo/
Cargo.toml
src/
main.rs[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2024"
[dependencies]cargo run reads the manifest, selects the binary target at src/main.rs,
builds in the dev profile, writes artifacts under target/debug, and runs the
resulting executable. If the package later gains a dependency, Cargo resolves it
from the configured registry, records the exact version in Cargo.lock, and
compiles dependency crates before compiling the local crate.
Common errors
Running Cargo outside a package gives a manifest-discovery error:
error: could not find `Cargo.toml` in current directory or any parent directoryChange into the package root, pass --manifest-path path/to/Cargo.toml, or
create a package with Start Projects with cargo new.
When a package has multiple binaries, cargo run may need target selection:
error: `cargo run` could not determine which binary to runUse cargo run --bin name or set default-run = "name" in [package].
Best practice
- ✅ Start new Rust projects with
cargo neworcargo init; let the generated structure teach the default conventions. - ✅ Use
cargo runwhile learning and for binary crates; it compiles stale code and then executes the resulting binary. - ✅ Use Cargo Build Run Check Test as the daily command vocabulary:
cargo check,cargo build,cargo run, andcargo test. - ✅ Keep dependency declarations in Cargo.toml Manifest and let Cargo update Cargo.lock.
- ✅ Treat
Cargo.tomlas source andtarget/as disposable build output. - ✅ Use Cargo’s target flags (
--bin,--lib,--example,--test) before writing custom scripts for ordinary package selection. - ✅ Learn package/crate/target vocabulary early; it makes compiler and Cargo diagnostics much easier to read.
Pitfalls
- ⚠️ Assuming Cargo is only a dependency downloader. It also owns build profiles, target discovery, tests, docs, examples, and workspace conventions.
- ⚠️ Running generated binaries from memory instead of using
cargo run; the path undertarget/debugis platform-specific and easy to mistype. - ⚠️ Replacing Cargo with ad hoc shell scripts too early; that is often Using rustc Directly for Cargo-Sized Projects.
- ⚠️ Editing generated files under
target/or committing them as source. - ⚠️ Forgetting
--when passing arguments to a binary throughcargo run; arguments after--go to your program, not Cargo.
See also
rustup and Installation · Anatomy of a Cargo Project · Cargo Build Run Check Test · Cargo.toml Manifest · Cargo.lock · Packages and Crates · Cargo Workspaces · Start Projects with cargo new · Tooling & Getting Started
Sources
- The Rust Programming Language, ch. 1.3 “Hello, Cargo!” — the-book, https://doc.rust-lang.org/book/ch01-03-hello-cargo.html
- The Cargo Book — https://doc.rust-lang.org/cargo/
