All-in-One Tooling
Why Rust's Toolchain Is Special
In many programming languages, the tools needed for development are scattered. In C/C++, the build system (Make, CMake, Ninja, Meson, ...), package manager (Conan, vcpkg, ...), formatter (clang-format), and linter (clang-tidy, cppcheck) all exist as independent projects, and which tools a project adopts varies. Even in JavaScript, package managers alone include npm, yarn, pnpm, and bun, while build tools span webpack, Vite, esbuild, Rollup, and Turbopack -- the choices are overwhelming.
Rust has a clear answer to this problem: integrate virtually everything needed for development into a single tool called Cargo.
Cargo: More Than Just a Build Tool
In Bitterless Rust, we introduced Cargo as "a build tool and package manager." That's correct, but it's just the tip of the iceberg when it comes to Cargo's full capabilities.
Here's what Cargo handles:
| Command | Role |
|---|---|
cargo new / cargo init |
Project creation |
cargo build |
Build |
cargo run |
Build + run |
cargo check |
Type checking (no binary generation) |
cargo test |
Run tests |
cargo bench |
Run benchmarks |
cargo doc |
Generate documentation |
cargo fmt |
Code formatting |
cargo clippy |
Linting (static analysis) |
cargo add / cargo remove |
Dependency management |
cargo publish |
Publish to crates.io |
cargo install |
Install binary crates |
cargo update |
Update dependencies |
cargo tree |
Display dependency tree |
cargo fix |
Automatically apply compiler-suggested fixes |
All of these are integrated into a single tool. From project creation to testing, documentation generation, and package publishing -- it all works with just the cargo command.
The Benefits of This Unity
The benefits of a unified toolchain go beyond "less to remember."
Every Rust project has the same structure. Look at Cargo.toml to understand dependencies, find source code under src/, and run tests with cargo test. This assumption holds for any Rust project you find on GitHub.
This is unthinkable in the C/C++ world. Projects using CMake, projects using Meson, projects using Autotools -- just understanding the build process is an ordeal.
cargo test: Integrated Testing
In Rust, tests can be written without introducing external test frameworks. You write tests directly in your source code:
fn add(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(1, 2), 3);
}
#[test]
fn test_add_negative() {
assert_eq!(add(-1, 1), 0);
}
}
#[cfg(test)] is a conditional compilation attribute meaning this module is only compiled during test execution. Functions with the #[test] attribute are recognized as test cases.
cargo test
running 2 tests
test tests::test_add ... ok
test tests::test_add_negative ... ok
test result: ok. 2 passed; 0 failed; 0 ignored
Because testing is integrated into the language and toolchain, every Rust library runs tests the same way. Just cargo test.
cargo bench: Benchmarks
Performance measurement is also integrated into Cargo. On nightly Rust you can use the standard benchmark feature, and on stable the criterion crate is common:
// benches/my_benchmark.rs (using criterion)
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn fibonacci(n: u64) -> u64 {
match n {
0 => 0,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
cargo bench
Project benchmarks run with a single cargo bench.
rustfmt: Unified Formatting
rustfmt is Rust's official formatter, invoked with cargo fmt:
cargo fmt
What's important is that rustfmt is official and has become the de facto standard in the Rust community.
Just as Go's gofmt ended the formatting wars, rustfmt has settled code style debates in Rust. Indentation is 4 spaces, brace placement goes here, use ordering goes like this -- none of these need to be discussed per project.
Customization via rustfmt.toml is possible, but many projects use the default settings as-is.
Clippy: Linting Beyond the Compiler
Clippy is Rust's official linter, invoked with cargo clippy:
cargo clippy
What makes Clippy particularly excellent is that it catches issues the compiler doesn't detect. Rust's compiler is already very strict, catching many type safety and memory safety issues at compile time, but Clippy goes even further.
Examples of what Clippy detects:
Non-Idiomatic Code
// Clippy warns about this
if x == true {
// ...
}
// Clippy's suggestion
if x {
// ...
}
Performance Issues
// Clippy warns: unnecessary clone
let s = some_string.clone();
println!("{}", s);
// -> Cloning where a reference would suffice
// Clippy warns: collect then len
let count = v.iter().filter(|x| x > 0).collect::<Vec<_>>().len();
// -> Should use .count()
let count = v.iter().filter(|x| x > 0).count();
Potential Bugs
// Clippy warns: floating point equality comparison
if x == 0.0 {
// ...
}
// Clippy's suggestion: compare using epsilon
if x.abs() < f64::EPSILON {
// ...
}
As of 2025, Clippy has over 700 lints, categorized as follows:
| Category | Description |
|---|---|
clippy::correctness |
Code that is almost certainly a bug |
clippy::suspicious |
Suspicious code that may not be a bug |
clippy::style |
Non-idiomatic code |
clippy::complexity |
Unnecessarily complex code |
clippy::perf |
Code where performance can be improved |
clippy::pedantic |
Stricter lints |
clippy::nursery |
Experimental lints |
Adding cargo clippy -- -D warnings to CI treats Clippy warnings as errors, continuously maintaining code quality.
cargo doc: Documentation Generation
Documentation generation is covered in detail in the Documentation chapter, but here we'll note that it's "integrated into Cargo."
cargo doc --open
This command generates HTML documentation for all public APIs in the project and opens it in a browser. Documentation for dependency crates is also generated together, so all documentation is available offline.
rustup: Toolchain Version Management
Outside of Cargo, there's rustup. This is a version manager for the Rust toolchain itself:
# Switch between stable/beta/nightly
rustup default stable
rustup default nightly
# Add cross-compilation targets
rustup target add wasm32-unknown-unknown
rustup target add aarch64-unknown-linux-gnu
# Add components
rustup component add clippy
rustup component add rustfmt
rustup component add rust-analyzer
What rustup manages:
- Rust compiler (rustc) version
- Cargo version
- Standard library version
- Target platform support
- Components (clippy, rustfmt, rust-analyzer, etc.)
Think of it as nvm and npm combined for Node.js, with added cross-compilation toolchain management.
rust-analyzer: IDE Support
rust-analyzer is Rust's official Language Server Protocol (LSP) implementation, installable via rustup component add rust-analyzer.
Because the LSP server is officially provided, whether you use VS Code, Neovim, Emacs, or IntelliJ, you get equivalent completion, type display, and refactoring support.
Summary: The Value of "One Way"
The excellence of Rust's toolchain lies not just in the quality of individual tools, but in the fact that the entire ecosystem operates in a unified way.
For any Rust project:
cargo buildto buildcargo testto run testscargo clippyto lintcargo fmtto formatcargo docto generate documentation
This unity has tremendous impact not just on individual developer experience, but also on team development and OSS maintenance. When joining a new project, you don't need to learn the build system. CI configuration becomes predictable. Style debates in code review become unnecessary.
This is one of the most successful embodiments of Rust's design philosophy: provide one right way to do things, and make it the default.