The Cargo Guide
Lints, Fixups, and Maintenance Commands
Why These Commands Matter
Cargo is not only a builder and package manager. It also sits at the center of several maintenance workflows: automatic fixups, formatting, linting, and edition migration. A useful mental model is:
cargo fixapplies compiler-driven code suggestionscargo fmtruns Rust formatting conventions throughrustfmtcargo clippyruns extra lints through Clippy
Together, these commands help keep a codebase buildable, readable, idiomatic, and easier to migrate over time.
Cargo as Orchestrator vs Tool Owner
One important distinction is that Cargo does not own all of these tools equally.
cargo fix is a true Cargo subcommand that works by driving cargo check and applying compiler suggestions.
By contrast, cargo fmt and cargo clippy are external Cargo commands distributed with the Rust toolchain as optional components. They are invoked through Cargo's command model, but the actual formatting and lint logic belongs to rustfmt and Clippy rather than to Cargo itself.
A useful mental model is:
- Cargo directly orchestrates fixup workflow for
cargo fix - Cargo is a convenient front door for
rustfmtand Clippy
A Small Example Package
Suppose we start with a small package:
cargo new maintenance_demo --lib
cd maintenance_demoManifest:
[package]
name = "maintenance_demo"
version = "0.1.0"
edition = "2024"Library source:
pub fn add(a:i32,b:i32)->i32{a+b}This tiny crate is enough to demonstrate formatting, linting, and automated fix suggestions.
cargo fix at a High Level
cargo fix applies compiler suggestions emitted by rustc diagnostics. It is designed to automate fixes the compiler already knows how to describe.
Example:
cargo fixA useful mental model is:
cargo fixis not a general refactoring engine- it applies actionable suggestions coming from the compiler's diagnostics pipeline
How cargo fix Works
cargo fix runs through the cargo check pipeline under the hood and applies fixable suggestions to source files.
That means it is fundamentally tied to code that Cargo can analyze through normal checking. If some code is hidden behind inactive features or inactive target-specific cfg conditions, cargo fix will not see or change that code unless you activate those configurations explicitly.
A Simple cargo fix Example
Suppose src/lib.rs contains code that triggers a compiler warning with a machine-applicable suggestion.
Illustrative source:
pub fn greet() {
let mut name = "alice";
println!("{name}");
}Then a normal fix workflow is:
cargo fixAfter the run, Cargo may have applied one or more compiler-suggested changes automatically and then shown any remaining warnings.
Feature-Conditioned Fixups
cargo fix only fixes code that is actually compiled during the check. That means feature-controlled code often requires explicit feature selection.
Example manifest:
[features]
default = []
json = []Feature-gated code:
#[cfg(feature = "json")]
pub fn output_mode() -> &'static str {
"json"
}To ensure that code is analyzed and eligible for fixes:
cargo fix --features jsonFor larger crates, it is often useful to run fixups across multiple feature configurations rather than assuming the default feature set is enough.
Target-Conditioned Fixups
Platform-specific code also needs explicit target selection if you want cargo fix to analyze it.
Example:
#[cfg(windows)]
pub fn platform_name() -> &'static str {
"windows"
}To fix code for a target-specific branch:
cargo fix --target x86_64-pc-windows-gnuA useful mental model is:
cargo fixonly sees code in the active compilation universe- features and targets define that universe
Target Selection for cargo fix
By default, cargo fix behaves like cargo check --all-targets, which means it considers all targets unless you narrow the selection.
Examples:
cargo fix --lib
cargo fix --bin app
cargo fix --test api
cargo fix --example quickstart
cargo fix --bench perfThis is especially useful in packages with many binaries, tests, benches, or examples, where you want to limit fixups to a specific part of the crate.
Workspace-Oriented Fixups
In workspaces, cargo fix follows normal Cargo package-selection rules.
Examples:
cargo fix --workspace
cargo fix -p core
cargo fix --workspace --exclude toolsA useful workflow for larger repositories is to apply fixes in smaller scopes rather than trying to change the whole workspace at once.
Broken-Code Mode
Sometimes the compiler's suggested fix is close but not fully correct in context. In those cases, cargo fix normally backs out failed edits.
If you want Cargo to leave the partially fixed code in place so you can inspect and finish it manually, use:
cargo fix --broken-codeThis is especially useful during large migrations where some automated changes are right in spirit but still need human cleanup.
cargo fmt at a High Level
cargo fmt formats the crate's source using rustfmt.
Example:
cargo fmtA useful mental model is:
- formatting is about code shape, not semantics
cargo fmtis the Cargo entry point forrustfmt
Because cargo fmt is an external Cargo command provided as an optional component, it may require installation through the toolchain if it is not already available.
A Simple Formatting Example
Suppose src/lib.rs contains poorly formatted code:
pub fn add(a:i32,b:i32)->i32{a+b}Then:
cargo fmtcan rewrite it into standard Rust style:
pub fn add(a: i32, b: i32) -> i32 {
a + b
}A useful principle is that formatting should generally be automatic and boring.
Formatting as a Maintenance Primitive
Formatting is often the lowest-friction maintenance command because it tends to reduce meaningless diffs and style arguments.
A healthy workflow often treats cargo fmt as routine rather than exceptional.
Example:
cargo fmt
cargo testThis keeps code shape consistent before deeper review or linting.
cargo clippy at a High Level
cargo clippy runs Clippy lints for a package.
Example:
cargo clippyA useful mental model is:
- compiler warnings catch some issues
- Clippy adds a broader layer of style, correctness, and idiom-focused lints
Like cargo fmt, cargo clippy is an external Cargo command distributed as an optional component rather than something built directly into Cargo.
A Simple Clippy Example
Suppose src/lib.rs contains code that works but is not especially idiomatic.
Illustrative example:
pub fn is_large(n: i32) -> bool {
if n > 10 {
return true;
}
false
}Then:
cargo clippymay suggest a simpler and more idiomatic structure.
A useful mental model is:
- Clippy is not only about correctness
- it is also about improving maintainability and idiomatic expression
Clippy in Everyday Workflow
A common maintenance rhythm is:
cargo fmt
cargo clippy
cargo testThis sequence means:
- normalize formatting first
- surface lint advice second
- verify behavior third
It is not the only order, but it is a practical one because formatting noise does not distract from lint and test review.
When cargo fix and Clippy Overlap
cargo fix and Clippy both interact with lint-driven improvement, but they are not the same thing.
A useful distinction is:
cargo fixapplies machine-applicable compiler suggestionscargo clippyreports additional lints, many of which still require judgment or manual edits
In practice, a maintenance session may use both:
cargo fix
cargo clippyEdition Migration Workflows
cargo fix also supports edition migration. The high-level migration flow is:
- run
cargo fix --edition - update the package's
editionfield inCargo.toml - run tests and follow-up checks
A minimal package manifest before migration might look like this:
[package]
name = "edition_demo"
version = "0.1.0"
edition = "2021"Then the migration command is:
cargo fix --editionAfter that, the edition field is updated manually:
[package]
name = "edition_demo"
version = "0.1.0"
edition = "2024"Why Edition Migration Is Not Fully Automatic
cargo fix --edition can automate many compiler-known changes, but it does not rewrite everything in every case.
A useful mental model is:
- Cargo can apply compiler-known migrations
- some edition changes still need human judgment
- inactive features, inactive targets, and macro-heavy code may require extra work
Edition Idioms
Cargo also supports edition-idiom updates for the current edition.
Example:
cargo fix --edition-idiomsThis is useful when a codebase already targets a given edition but still wants to adopt the preferred style and idioms associated with that edition more consistently.
Advanced Migration in Large Workspaces
Large projects and workspaces do not always migrate all at once. A useful incremental model is:
- migrate one package at a time in a workspace
- or migrate individual targets one at a time within a package
This is especially practical when multiple binaries, tests, or examples make a full migration noisy or risky.
Migrating One Target at a Time
Suppose a package has multiple binaries and examples.
Layout:
edition_demo/
āāā Cargo.toml
āāā src/
ā āāā lib.rs
ā āāā main.rs
ā āāā bin/
ā āāā admin.rs
āāā examples/
āāā quickstart.rsThen a narrower migration command may be useful:
cargo fix --edition --bin admin
cargo fix --edition --example quickstartThis helps isolate migration work to smaller surfaces.
Allowing Dirty or Staged Working Trees
cargo fix is conservative about modifying source trees. If your working directory already has changes, Cargo may refuse to proceed unless you opt in.
Examples:
cargo fix --allow-dirty
cargo fix --allow-stagedThis is useful in practiced workflows, but it is healthiest when you understand exactly what changes are already present.
A Practical Maintenance Workflow
For many crates, a healthy maintenance pass looks something like this:
cargo fmt
cargo fix
cargo clippy
cargo testAnd for a featureful or multi-target package:
cargo fix --all-features
cargo fix --target x86_64-pc-windows-gnu
cargo clippy --all-features
cargo test --all-featuresThis keeps formatting, compiler-driven fixes, lint review, and behavior verification in one coherent loop.
A Workspace Maintenance Workflow
In a workspace, it is often useful to combine package selection with maintenance commands.
Examples:
cargo fix --workspace
cargo clippy --workspace
cargo test --workspaceOr narrower:
cargo fix -p core
cargo clippy -p core
cargo test -p coreThis is especially valuable in larger monorepos where one package's maintenance changes should not automatically touch every member.
Automated Suggestions vs Human Judgment
A healthy mental model is that automated suggestions are assistance, not automatic truth.
cargo fix can apply machine-applicable compiler suggestions, and Clippy can surface many useful improvements, but a maintainer still needs to judge whether a suggested change improves clarity, preserves intended semantics, and fits the crate's public API commitments.
A Small End-to-End Example
Suppose src/lib.rs starts like this:
pub fn add(a:i32,b:i32)->i32{a+b}
pub fn is_large(n: i32) -> bool {
if n > 10 {
return true;
}
false
}A small maintenance session could look like:
cargo fmt
cargo fix
cargo clippy
cargo testAfter formatting and lint-guided cleanup, the code may become easier to read and maintain even if the core behavior did not change.
When rustfmt and Clippy Are Logically Separate
Although cargo fmt and cargo clippy are invoked through Cargo, they remain logically separate tools.
A useful distinction is:
- Cargo gives them package and workspace context
rustfmtowns formatting rules- Clippy owns its extra lint set
This matters because understanding a result sometimes means asking whether the behavior came from Cargo orchestration or from the external tool's own rules and configuration.
Common Beginner Mistakes
Mistake 1: treating cargo fix like a general-purpose refactoring engine instead of a compiler-suggestion applicator.
Mistake 2: forgetting that cargo fix only sees code in the active feature and target configuration.
Mistake 3: assuming cargo fmt and cargo clippy are built into Cargo rather than optional external Cargo commands.
Mistake 4: trying to migrate editions without re-testing after updating Cargo.toml.
Mistake 5: applying every lint suggestion without judgment.
Mistake 6: running maintenance commands across a large workspace without scoping them deliberately.
Hands-On Exercise
Create a small library crate and run a complete maintenance pass.
Start here:
cargo new maintenance_lab --lib
cd maintenance_labUse this source:
pub fn add(a:i32,b:i32)->i32{a+b}
pub fn is_large(n: i32) -> bool {
if n > 10 {
return true;
}
false
}Then run:
cargo fmt
cargo fix
cargo clippy
cargo testIf rustfmt or Clippy are not installed in your current toolchain, install their toolchain components first and rerun. Then compare the code before and after to see which changes came from formatting, which came from compiler-driven fixes, and which came from lint review.
Mental Model Summary
A strong mental model for lints, fixups, and maintenance commands in Cargo is:
cargo fixis Cargo's compiler-suggestion application workflow, built aroundcargo checkcargo fmtandcargo clippyare external Cargo commands that Cargo orchestrates but does not logically own- formatting, linting, and fixups are related but distinct maintenance surfaces
- edition migration is a structured workflow centered on
cargo fix --edition, manual manifest update, and follow-up testing - larger crates and workspaces benefit from deliberate feature, target, and package scoping during maintenance
Once this model is stable, Cargo maintenance commands become much easier to use as a disciplined codebase hygiene workflow instead of as isolated convenience commands.
