Wonderfull Rust

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 build to build
  • cargo test to run tests
  • cargo clippy to lint
  • cargo fmt to format
  • cargo doc to 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.

Related

Back to book