Chapter 2 - Clippy and Linting Discipline
Be sure to have cargo clippy installed with your rust compiler, run cargo clippy -V in your terminal for a rust project and you should get something like this clippy 0.1.86 (05f9846f89 2025-03-31). If terminal fails to show a clippy version, please run the following code rustup update && rustup component add clippy.
Clippy documentation can be found here.
2.1 Why care about linting?
Rust compiler is a powerful tool that catches many mistakes. However, some more in-depth analysis require extra tools, that is where cargo clippy clippy comes into to play. Clippy checks for: * Performance pitfalls. * Style issues. * Redundant code. * Potential bugs. * Non-idiomatic Rust.
2.2 Always run cargo clippy
Add the following to your daily workflow:
$ cargo clippy --all-targets --all-features --locked -- -D warnings* --all-targets: checks library, tests, benches and examples. * --all-features: checks code for all features enabled, auto solves conflicting features. * --locked: Requires Cargo.lock to be up-to-date, can be solved with $ cargo update. * -D warnings: treats warnings as errors
Potential additions elements to add:
* -- -W clippy::pedantic: lints which are rather strict or have occasional false positives. * -- -W clippy::nursery: Optionally can be added to check for new lints that are still under development. * ❗ Add this to your Makefile, Justfile, xtask or CI Pipeline.
Example at ApolloGraphQL
>
In the
Routerproject there is axtaskconfigured for linting that can be executed withcargo xtask lint.
2.3 Important Clippy Lints to Respect
| Lint Name | Why | Link |
|---|---|---|
redundant_clone | Detects unnecessary clones, has performance impact | link (nursery + perf) |
needless_borrow group | Removes redundant & borrowing | link (style) |
map_unwrap_or / map_or | Simplifies nested Option/Result handling | map_unwrap_or unnecessary_map_or unnecessary_result_map_or_else |
manual_ok_or | Suggest using .ok_or_else instead of match | link (style) |
large_enum_variant | Warns if an enum has very large variant which is bad for memory. Suggests Boxing it | link (perf) |
unnecessary_wraps | If your function always returns Some or Ok, you don't need Option/Result | link (pedantic) |
clone_on_copy | Catches accidental .clone() on Copy types like u32 and bool | link (complexity) |
needless_collect | Prevents collecting and allocating an iterator, when allocation is not needed | link (nursery) |
2.4 Fix warnings, don't silence them!
NEVER just #[allow(clippy::lint_something)] unless:
* You truly understand why the warning happens and you have a reason why it is better that way. * You document why it is being ignored. * ❗ Don't use allow, but expect, it will give a warning in case the lint is not true anymore, #[expect(clippy::lint_something)].
Example:
// Faster matching is preferred over size efficiency
#[expect(clippy::large_enum_variant)]
enum Message {
Code(u8),
Content([u8; 1024]),
}The fix would be:
>
// Faster matching is preferred over size efficiency #[expect(clippy::large_enum_variant)] enum Message { Code(u8), Content(Box<[u8; 1024]>), }
Handling false positives
Sometimes Clippy complains even when your code is correct, in those cases there are two solutions:
- Try to refactor the code, so it improves the warning.
- Locally override the lint with
#[expect(clippy::lint_name)]and a comment with the reason. - Avoid global overrides, unless it is core crate issue, a good example of this is the Bevy Engine that has a set of lints that should be allowed by default.
2.5 Configure workspace/package lints
In your Cargo.toml file it is possible to determine which lints and their priorities over each other. In case of 2 or more conflicting lints, the higher priority one will be chosen. Example configuration for a package:
[lints.rust]
future-incompatible = "warn"
nonstandard_style = "deny"
[lints.clippy]
all = { level = "deny", priority = 10 }
redundant_clone = { level = "deny", priority = 9 }
manual_while_let_some = { level = "deny", priority = 4 }
pedantic = { level = "warn", priority = 3 }And for a workspace:
[workspace.lints.rust]
future-incompatible = "warn"
nonstandard_style = "deny"
[workspace.lints.clippy]
all = { level = "deny", priority = 10 }
redundant_clone = { level = "deny", priority = 9 }
manual_while_let_some = { level = "deny", priority = 4 }
pedantic = { level = "warn", priority = 3 }