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 comes into 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 with all features enabled; it does not auto-solve conflicting features.--locked: RequiresCargo.lockto be up-to-date, can be solved with$ cargo update.-D warnings: treats warnings as errors
Potential additional 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, butexpect, 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 }