Tooling
In one sentence
Section titled “In one sentence”Cargo includes the compiler, the formatter, the linter, the test runner, and the documentation generator, so one short command sequence checks an agent’s change before you read it.
The Java you know
Section titled “The Java you know”A Java project combines many tools: Maven or Gradle, javac, Checkstyle or Spotless for formatting, SpotBugs or SonarQube for lints, JUnit through Surefire, Javadoc, and the OWASP dependency check. Each has its own configuration. In Rust, the official tools come with the toolchain, and they all run through cargo.
The Rust you will see
Section titled “The Rust you will see”| Command | What it does | Java |
|---|---|---|
cargo check |
type-check without building a binary: the fastest feedback | javac without packaging |
cargo build |
compile (add --release for an optimized build) |
mvn package |
cargo run |
build and run the binary | mvn exec:java |
cargo test |
build and run unit, integration, and doc tests | mvn test |
cargo fmt |
format the code (--check to only report) |
Spotless, google-java-format |
cargo clippy |
lints: more than 800 checks for bugs, style, and performance | SpotBugs, Checkstyle, Sonar |
cargo doc --open |
build the API documentation of the crate and all dependencies | Javadoc |
cargo tree |
the dependency tree | mvn dependency:tree |
cargo fmt has almost no options, so all Rust code looks the same. In review, never discuss formatting: ask for cargo fmt.
How to read it
Section titled “How to read it”A compiler error
Section titled “A compiler error”Rust compiler errors are long, and they are worth reading in full. This is the real output for a value used after a move:
error[E0382]: use of moved value: `report` --> borrow_error.rs:10:23 | 8 | pub fn send_twice(report: String) -> usize { | ------ move occurs because `report` has type `String`, which does not implement the `Copy` trait 9 | let first = send(report); | ------ value moved here10 | let second = send(report); | ^^^^^^ value used here after move |note: consider changing this parameter type in function `send` to borrow instead if owning the value isn't necessary --> borrow_error.rs:4:21 | 4 | pub fn send(report: String) -> usize { | ---- ^^^^^^ this parameter takes ownership of the value | | | in this functionhelp: consider cloning the value if the performance cost is acceptable | 9 | let first = send(report.clone()); | ++++++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0382`.Read it from the top:
- The error code and the summary:
E0382, “use of moved value”.rustc --explain E0382prints a full explanation with examples. All codes are in the compiler error index. - The labels in the code: where the value was declared, where it moved, and where it was used after the move. The labels tell a story in order.
- The
noteandhelplines: suggestions. Here the compiler offers two fixes: borrow insend, or clone. The first is usually right. An agent often takes the second, because it is the last suggestion and the smallest change. See S01:clone()to satisfy the borrow checker.
A Clippy warning
Section titled “A Clippy warning”warning: the loop variable `i` is only used to index `prices` --> clippy_warning.rs:6:14 |6 | for i in 0..prices.len() { | ^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.99.0/index.html#needless_range_loop = note: `-W clippy::needless-range-loop` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::needless_range_loop)]`help: consider using an iterator |6 - for i in 0..prices.len() {6 + for <item> in &prices { |
warning: 1 warning emitted- The first line says what is wrong.
- The link goes to the lint’s documentation, with an explanation and examples. The lint name is at the end of the URL:
needless_range_loop. - The
notesays why the lint is on: here, it is in the default group. - The
helpwith a diff is a suggested fix. Many Clippy fixes can be applied withcargo clippy --fix. - “to override … add
#[allow(...)]” is how to silence it. That is almost never the right response to a warning in agent code. See S25:#[allow(...)]to silence clippy or compiler warnings.
Both files are captured from the pinned toolchain by examples/tooling/capture.sh, and CI checks that they are current.
Clippy lint groups
Section titled “Clippy lint groups”| Group | Default | Contains |
|---|---|---|
correctness |
deny | code that is wrong |
suspicious, style, complexity, perf |
warn | likely bugs, non-idiomatic code, needless complexity, slow code |
pedantic |
allow | strict lints, with some false positives |
nursery |
allow | new lints that are still in development |
restriction |
allow | lints against specific features, such as unwrap_used. Enable them one by one, never as a group |
cargo |
allow | checks on Cargo.toml |
A project turns groups and single lints on in Cargo.toml. This is the configuration of the examples on this site:
[workspace.lints.rust]unsafe_code = "deny"
[workspace.lints.clippy]pedantic = { level = "warn", priority = -1 }# Exceptions to pedantic. Each one adds noise to short teaching snippets# without changing what the reader learns. The site discusses these topics# where they matter instead.must_use_candidate = "allow" # `#[must_use]` on every pure functionreturn_self_not_must_use = "allow" # `#[must_use]` on every builder methodmissing_errors_doc = "allow" # `# Errors` doc sections in every snippetmissing_panics_doc = "allow" # `# Panics` doc sections in every snippetimplicit_hasher = "allow" # generic `S: BuildHasher` on every `HashMap` parameterSupply chain and other tools
Section titled “Supply chain and other tools”These are installed separately with cargo install <name>. They are well known in the Rust community:
| Tool | What it does | Java |
|---|---|---|
cargo audit |
checks Cargo.lock against the RustSec database of security advisories |
OWASP dependency-check |
cargo deny |
rules for licenses, advisories, banned crates, and duplicate versions | license and enforcer plugins |
cargo outdated |
lists dependencies with newer versions | versions:display-dependency-updates |
cargo machete |
finds dependencies that the code does not use | dependency:analyze |
cargo expand |
shows the code after macro expansion (see Macros) | (generated sources) |
cargo nextest |
a faster test runner with better output | Surefire options |
cargo miri |
runs tests in an interpreter that finds undefined behavior in unsafe code. Needs a nightly toolchain |
(none) |
The editor
Section titled “The editor”Use rust-analyzer in your editor. It works in VS Code and most other editors. JetBrains RustRover has its own engine with the same features. For reading agent code, two features help more than any other: hover, which shows the type and documentation of anything, and inlay hints, which show the inferred type of each let and closure parameter. Rust code leaves out many types, and the hints put them back.
What good looks like
Section titled “What good looks like”A review gate that runs before you read the code. Each step must pass:
# Run this in the project before you read an agent's Rust code.# Each step must pass. Stop at the first failure.set -euo pipefail
cargo fmt --all --check # formatting: no diffcargo clippy --workspace --all-targets -- -D warnings # lints: every warning is an errorcargo test --workspace # unit, integration, and doc testscargo fmt --check: the code is formatted, so the diff shows only real changes.cargo clippy ... -D warnings: no warning is left.--all-targetsincludes tests and examples.cargo test: all tests pass, including doc tests.
Add cargo audit and cargo deny check in CI for projects with many dependencies.
Red flags in AI output
Section titled “Red flags in AI output”- The agent says “it compiles” or “tests pass”, but did not run Clippy. Run the gate yourself.
#[allow(...)]added to make Clippy quiet, or a[lints]section that turns lints off. See S25:#[allow(...)]to silence clippy or compiler warnings.- A CI job that runs
cargo buildandcargo testonly. - Changes to
rust-toolchain.toml, or a nightly toolchain, with no reason. - New dependencies that
cargo treeshows to pull in many more crates. See S28: Too many or unknown dependencies.
- S25Severity: Medium
#[allow(...)]to silence clippy or compiler warningsA warning is hidden instead of fixed, with no reason given.
Better vs worse
Section titled “Better vs worse”The task: the check that runs before a change is reviewed.
# "It builds and the tests pass."cargo buildcargo test# Run this in the project before you read an agent's Rust code.# Each step must pass. Stop at the first failure.set -euo pipefail
cargo fmt --all --check # formatting: no diffcargo clippy --workspace --all-targets -- -D warnings # lints: every warning is an errorcargo test --workspace # unit, integration, and doc testsThe “worse” check misses every Clippy warning, and formatting changes make the diff noisy. Without set -e, a failed build does not even stop the script. The “better” gate stops at the first failure, and treats warnings as errors. It takes a few seconds more, and it removes a whole class of review comments.
Check yourself
Section titled “Check yourself”Which command gives the fastest feedback on whether code type-checks?
Show the answer
cargo check. It runs the compiler’s checks without generating a binary.The compiler says
help: consider cloning the value. Should you accept that fix?Show the answer
Not by default. Read the other suggestions first. Borrowing instead of taking ownership is usually the better fix. A clone is right only when two owners really need their own copy.
Which Clippy groups are on by default?
Show the answer
correctness(as errors), andsuspicious,style,complexity, andperf(as warnings).pedantic,nursery,restriction, andcargoare off.What does
-D warningsdo incargo clippy -- -D warnings?Show the answer
It turns every warning into an error, so the command fails if any warning is left.
Go deeper
Section titled “Go deeper”- The Cargo Book
- The Clippy book, with the list of lint groups and configuration
- Rust compiler error index
- The rustc book: Lints