How to Use cargo clippy for Linting in Rust

Run `cargo clippy` in your project root to analyze your code for common mistakes, performance issues, and style violations that the compiler misses.

The second pair of eyes

You finish a feature. cargo test passes. cargo build is clean. You push the code. Two days later, a review points out three things: you're cloning a vector unnecessarily, you're comparing an Option with == instead of using a pattern, and your loop is doing work it doesn't need to. The compiler didn't catch any of this. The compiler thinks your code is fine. You need a second pair of eyes that never sleeps.

That is what cargo clippy provides. It is the community-maintained linter for Rust. It catches mistakes, performance traps, and style issues that the compiler ignores. It helps you write code that is not just correct, but idiomatic.

What Clippy actually checks

The Rust compiler guarantees memory safety and type correctness. If rustc accepts your code, it will not segfault, it will not use freed memory, and it will not violate type rules. That is a massive win. The compiler does not care about idioms, performance traps, or readability. Clippy fills that gap. It is a linter that knows Rust inside out. It checks for patterns that compile but are wrong, slow, or confusing.

Think of the compiler as a strict gatekeeper checking your ID. Clippy is the experienced mentor walking beside you, pointing out shortcuts and traps. The gatekeeper ensures you can enter the building. The mentor ensures you don't trip over the rug in the lobby.

Clippy checks for things like:

  • Redundant clones that waste memory and CPU cycles.
  • Non-idiomatic patterns like if x == None instead of if x.is_none().
  • Performance traps like iterating by value when a reference would do.
  • Style issues like missing doc comments or inconsistent naming.
  • Logic errors like comparing floating point numbers with ==.

Run Clippy before every commit. It catches bugs that cost hours to debug later.

Minimal example: idiomatic traps

Clippy flags code that works but isn't the best way to do it. Here is a simple example.

fn check_list(items: &[i32]) {
    // Clippy warns here. items.len() > 0 works, but !items.is_empty()
    // is the standard idiom. It is clearer and sometimes faster.
    if items.len() > 0 {
        println!("List has items");
    }
}

Run cargo clippy. You get a warning.

warning: this `.len() > 0` can be written more succinctly as `!..is_empty()`

Clippy suggests the fix. The compiler saw no problem. Clippy sees the idiom. The warning points to the line and explains why the change is better. It also offers a fix command.

Treat the lint list as a checklist. Clear it to zero warnings.

How Clippy analyzes your code

Clippy is not a magic spell. It is a tool that runs your code through a modified version of the compiler. It analyzes the MIR, the intermediate representation Rust generates before turning code into machine instructions. Because it sees the MIR, Clippy can understand the logic of your program, not just the syntax.

It can detect that a loop variable never changes. It can see that a function returns a value that gets immediately dropped. It can trace data flow across function calls. This deep analysis allows Clippy to catch issues that simple text-based linters miss.

Clippy runs as part of the cargo workflow. Install it with rustup component add clippy. It integrates seamlessly with your build process. You run cargo clippy just like you run cargo build. It feels like a build step because it is one.

Trust Clippy. It usually knows more idioms than you do.

Lint categories and configuration

Clippy groups lints into categories. You can enable or disable categories to control how strict the checks are.

  • clippy::all is the default set. It catches common mistakes and performance issues. This is what runs when you execute cargo clippy without flags.
  • pedantic enables stricter checks for style and correctness. It catches missing doc comments, redundant clones, and non-idiomatic patterns. This is useful for libraries and public APIs.
  • nursery contains experimental lints. They may have false positives. Use these only if you are testing new checks.
  • cargo checks your Cargo.toml for issues like missing versions or redundant dependencies.

You can enable categories via command line or configuration. The command line flag -- -W clippy::pedantic enables pedantic lints as warnings. This is handy for quick checks.

For permanent configuration, use Cargo.toml. This keeps your settings version-controlled and shared across the team.

Realistic setup: Cargo.toml and conventions

Add a [lints.clippy] section to your Cargo.toml to configure Clippy. This is the standard way to manage lints in a project.

[lints.clippy]
# Enable pedantic lints. These are stricter checks for style and correctness.
# They catch things like missing doc comments or redundant clones.
pedantic = { level = "warn", priority = -1 }

# Allow specific lints that pedantic enables but you disagree with.
# module_name_repetitions is noisy in small projects.
module_name_repetitions = "allow"

# Enforce doc comments on public items.
missing_errors_doc = "warn"

Convention aside: always set priority = -1 on category lints like pedantic. Clippy resolves conflicts by priority. If you enable pedantic without a priority, a specific lint override might get ignored. The negative priority tells Clippy to apply the category first, then let your specific rules win. This ensures your allowances actually work.

Sometimes you need to allow a lint in code. Use #[allow(clippy::lint_name)]. Convention: always provide a reason.

// Convention: always provide a reason when allowing a lint.
// This tells future readers that the violation is intentional.
#[allow(clippy::len_without_is_empty, reason = "is_empty is implemented in a trait")]
pub fn len(&self) -> usize {
    self.0.len()
}

Silencing a lint without a reason hides bugs and confuses reviewers. If you allow a lint, write a comment explaining why.

Pitfalls and the --fix trap

Clippy can fix issues automatically. Run cargo clippy --fix to apply corrections. This modifies your source files. It is powerful but dangerous.

If you have uncommitted changes, cargo clippy --fix will refuse to run. You get an error about uncommitted changes. Add --allow-dirty to proceed.

cargo clippy --fix --allow-dirty

Pitfall: --fix might apply a fix that breaks logic in edge cases. Always review diffs. Never blindly commit --fix output. Run --fix on a clean branch, or review every change carefully. The tool is smart, but it is not perfect.

Another pitfall is over-reliance on --allow. If you start allowing lints everywhere, Clippy becomes useless. Allow lints only when you have a good reason. Prefer fixing the code to silencing the warning.

In CI, enforce standards. Use cargo clippy -- -D warnings. This turns warnings into errors. The build fails if Clippy complains. This ensures new code adheres to quality guidelines. Treat warnings as bugs. Fix them before they become tech debt.

Decision: when to use Clippy

Use cargo clippy when you want to catch idiomatic errors, performance traps, and style issues that the compiler ignores. Use cargo clippy -- -D warnings in CI to enforce quality standards and block merges on lint violations. Use cargo clippy --fix to apply automatic corrections, but only after reviewing the diff or running on a clean branch. Reach for rustc checks when you need to verify type safety and memory guarantees; Clippy complements the compiler, it does not replace it. Reach for cargo fmt when you need consistent formatting; Clippy handles logic and style, rustfmt handles whitespace and indentation. Pick pedantic lints for libraries or public APIs where documentation and strict style matter. Pick nursery lints only if you are testing experimental checks; they may have false positives.

Run Clippy locally before pushing. It saves review cycles and keeps the codebase clean.

Where to go next