So far you've compiled a single file by hand with rustc. That's fine for a toy example, but no real Rust project works that way. The moment you have more than one file, or want to use code someone else wrote, you want Cargo — and honestly, you want it from line one anyway.
What Cargo actually does
Cargo is Rust's build tool and package manager, rolled into one binary you already have (it came with rustup). It:
- compiles your project with one command,
- downloads and builds your dependencies (called "crates") from crates.io,
- runs your tests, generates documentation, and formats your code,
- and keeps a lockfile so builds are reproducible across machines.
If you've used npm, pip, or bundler before, Cargo will feel familiar — except it's faster, and it's the only tool you need.
Create a new project
Pick a folder you don't mind cluttering, then run:
Cargo creates a hello_cargo folder, drops a starter program inside, and even sets up a git repository for you. Open it in your editor and you'll see this:
Cargo.toml — your project's ID card
This file describes your project: its name, version, which edition of Rust it targets, and — eventually — its dependencies. Open it and you'll see something like:
TOML is a simple configuration format — think of it as a friendlier JSON for humans to edit by hand. The [dependencies] section is empty for now, but that's exactly where you'll add crates later in this lesson.
src/main.rs — your actual code
Cargo also wrote you a starter program, and it's the same "Hello, World!" you saw in the first lesson:
Cargo always expects your code to live under src/. Top-level files in your project folder — Cargo.toml, README.md, license files, CI configs — stay outside it. That separation makes projects easy to scan at a glance.
The commands you'll use constantly
From inside the project folder, here's your new daily toolkit:
cargo run
Compiles your code (if it's changed) and runs the result, in one step:
The first time, Cargo builds your project and saves the binary in target/debug/. After that, it only rebuilds what changed — so repeated runs are quick.
cargo build
Compiles your project without running it. Add --release when you want an optimized build for production:
cargo check
This is the one nobody tells you about early enough, and it'll save you real time. It asks the compiler "would this build?" without actually producing a binary — which makes it dramatically faster than a full build:
While you're actively writing code, run cargo check constantly. Save cargo run for when you actually want to see your program execute.
Try it yourself: open
src/main.rs, change the greeting to something else, and runcargo runagain. Notice how Cargo saysCompiling hello_cargo...before running it this time — it knows the source changed.
Adding your first dependency
This is where Cargo really shines. Let's add rand, a popular crate for generating random numbers — something the standard library deliberately leaves out. Open Cargo.toml and add it under [dependencies]:
Now use it in src/main.rs:
Run cargo run and watch what happens: Cargo notices the new dependency, downloads rand and everything it depends on, compiles all of it, and then compiles and runs your program — all from one command. Run it again, and it skips straight to running, because nothing changed.
What's that Cargo.lock file?
You'll notice a new Cargo.lock file appeared. It records the exact version of every dependency that got downloaded. You won't edit this by hand — Cargo manages it — but it's why "it works on my machine" is much rarer in the Rust world: everyone building your project gets identical dependency versions.
Quick reference
| Command | What it does |
|---|---|
cargo new my_project | Scaffold a new project |
cargo check | Fast compile-error check, no binary produced |
cargo build | Compile the project |
cargo run | Compile (if needed) and run |
cargo build --release | Compile an optimized production build |
That's genuinely most of what you need from Cargo day to day. The rest — testing, formatting, documentation — we'll pick up naturally as we go, starting with writing tests later in this course.
Next up, let's actually look at the building blocks of the language itself, starting with data types.