Introduction
By day 20 of your Rust journey, you've written functions, handled errors, and maybe even deployed something small. But your real productivity multiplier isn't advanced language features—it's mastering your build toolchain. Cargo essentials: what every Rust developer should know are the everyday commands that separate developers who fight their tools from those who work seamlessly within them.
We'll walk through the commands you'll use in production every single day, from building and testing to maintaining code quality and managing dependencies. These aren't flashy features. They're the foundation that lets you ship faster.
cargo build, run and test: your development loop
These three commands form the core of daily development. Let's start with what each does and when you actually use them.
cargo build compiles your project into an executable or library. By default, it targets debug mode—faster to compile, slower to run, full debug info. For a real-world scenario: you're building an HTTP service and want to iterate quickly. Debug builds give you that speed.
cargo buildThis places your binary in target/debug/. When you're ready to ship or benchmark, use:
cargo build --releaseRelease builds take longer to compile but run significantly faster—typically 10-100x depending on workload. We learned this the hard way with a data pipeline that seemed slow until someone remembered we were still running debug builds in staging.
cargo run builds and executes your binary immediately. During development, this is your daily driver. No separate compile and execute steps.
cargo run -- --config production.tomlThe -- separator passes arguments to your program, not to cargo. Forget this once and you'll remember it forever.
cargo test compiles tests in test mode and runs them. By default it runs all tests in parallel.
cargo testYou can filter tests by name:
cargo test database_This runs only tests with "database_" in their name. In a microservice with hundreds of tests, this saves mental cycles. You can also run a single test thread for ordering-dependent tests (unusual but happens):
cargo test -- --test-threads=1One pattern we use: run cargo test before every commit locally. It's cheap enough that skipping it eventually costs more in CI cycles.
cargo add for dependencies: the modern way
Manually editing Cargo.toml works, but cargo add is faster and smarter. It fetches the latest version, handles version constraints, and updates your lock file automatically.
cargo add tokioThis adds tokio with its latest version. Behind the scenes, cargo queries crates.io and figures out compatible versions. Your Cargo.toml gets updated, usually with a caret constraint like tokio = "^1.40".
Specify features if needed:
cargo add tokio --features fullOr a specific version:
cargo add serde --version "1.0.200"For development dependencies only:
cargo add --dev criterionThis adds criterion (a benchmarking framework) to [dev-dependencies], so it won't bloat production builds.
Remove dependencies just as cleanly:
cargo remove tokioWe've seen teams still manually editing TOML and checking what version is current by browsing crates.io in a browser tab. Cargo add eliminates that friction. It's one of the reasons newer Rust adoption is smoother than it was five years ago.
cargo fmt and cargo clippy: non-negotiable code quality
These two commands aren't optional if you're working in a team. They're your contract with your codebase.
cargo fmt automatically formats your code to Rust's standard style. Run it:
cargo fmtIt modifies files in place. No bikeshedding about where braces go or whether to use two or four spaces. Consistency is enforced. In projects with 20+ developers, this alone eliminates countless review comments.
Check if your code is formatted without modifying it:
cargo fmt -- --checkWe run this in CI—if formatting isn't correct, the build fails. Forces everyone to run cargo fmt locally before pushing.
cargo clippy is a linter that catches common mistakes and suggests idiomatic Rust patterns.
cargo clippyExample output from real code:
warning: you are using an explicit closure to convert something into `Result`
|
| .map(|v| Ok(v))
|
= note: Consider using `map_ok` insteadClippy isn't always right—sometimes you have intentional code that triggers warnings—but it catches the kinds of mistakes that cost hours in production. We treat clippy warnings as seriously as errors. Run it with:
cargo clippy -- -D warningsThe -D warnings flag denies warnings, treating them as errors. This is harsh but effective for maintaining code quality.
In your CI pipeline, both should be mandatory:
cargo fmt -- --check
cargo clippy -- -D warnings
cargo test
cargo build --releaseThis four-step pipeline takes minutes and catches 95% of preventable issues before they hit main.
Cargo.lock: managing reproducibility for applications
This one trips up developers who come from other ecosystems. Cargo.lock is a file that locks exact dependency versions. Whether you commit it depends on what you're building.
For applications (binaries): Always commit Cargo.lock. This ensures everyone on your team, and your deployment environment, uses identical dependency versions. Reproducibility matters. We had a situation where a transitive dependency released a minor version that broke our observability integration—but our staging environment had already pulled it while production hadn't. The lock file prevents this.
For libraries: Don't commit Cargo.lock. Users of your library generate their own lock file with their own dependency versions. Committing yours just adds noise in version control.
Check your Cargo.toml. If you have [[bin]] sections, you're writing an application. Commit the lock file.
When dependencies need updating, you have options. Update everything:
cargo updateOr target specific packages:
cargo update -p tokioThis respects version constraints in Cargo.toml—so if you specified tokio = "^1.30", it updates to the latest 1.x, not 2.0.
Before merging any update to main, run your full test suite. Dependency updates should be treated like any other code change.
Common mistakes
Forgetting -- in cargo run: cargo run --config dev tries to run with config as a cargo flag, not your program's argument. Use cargo run -- --config dev. Easy to miss, annoying to debug.
Running tests in release mode without realizing it: If you need faster tests, cargo test --release exists, but remember that release mode optimizations can hide bugs that debug mode would catch. Use sparingly.
Committing Cargo.lock for libraries: It bloats your repository and forces unnecessary updates on your users. Check whether you're building a library or application.
Ignoring clippy warnings: They're not decoration. They often point to subtle logic errors or performance issues. Even the "harmless" ones are usually easier to fix now than debug later.
Not using cargo add: We've seen developers manually edit Cargo.toml and get syntax wrong or forget to update the lock file. Cargo add handles all this. It's faster and more reliable.
Key takeaways
cargo build,cargo run, andcargo testform your development loop. Debug mode for speed, release mode for performance.cargo addandcargo removeare the modern way to manage dependencies—faster and more reliable than manual TOML editing.cargo fmtenforces consistent style across teams. Make it mandatory in CI, not optional.cargo clippycatches idiomatic and performance issues before code review. Treat warnings seriously.- Commit
Cargo.lockfor applications, not libraries. It ensures reproducible builds in production. - Wire all of these into your CI pipeline. Consistency and quality compound over time.






Comments
Loading comments…