Step 5 of 7
Clippy: fixing common lints
Clippy is Rust's official linter: hundreds of extra checks, on top of the compiler's, for code that is wrong, slow, or just not idiomatic. Every serious Rust team runs it, and most block pull requests that have clippy warnings. It comes with rustup (rustup component add clippy if it's missing):
$ cargo clippy
warning: the loop variable `i` is only used to index `prices`
--> src/main.rs:3:14
|
3 | for i in 0..prices.len() {
| ^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.97.0/index.html#needless_range_loop
= note: `#[warn(clippy::needless_range_loop)]` on by default
help: consider using an iterator
|
3 - for i in 0..prices.len() {
3 + for <item> in &prices {
|
Each warning names its lint (needless_range_loop), links to an explanation with examples, and usually suggests the fix. cargo clippy --fix applies the suggestions it's sure about.
Lint groups
| Group | Default | Catches |
|---|---|---|
clippy::correctness |
error | code that is almost certainly a bug |
clippy::suspicious |
warn | code that is probably a bug |
clippy::style |
warn | code that works but isn't idiomatic |
clippy::complexity |
warn | simple things done in a complicated way |
clippy::perf |
warn | code that could easily be faster |
clippy::pedantic |
off | stricter lints, with some false alarms |
clippy::nursery |
off | new lints still being polished |
clippy::restriction |
off | team rules, like banning unwrap or dbg! |
The lints you'll see most
| Lint | Instead of | Write |
|---|---|---|
needless_range_loop |
for i in 0..v.len() { v[i] } |
for x in &v, or .iter().enumerate() if you need i |
len_zero |
v.len() == 0 |
v.is_empty() |
needless_return |
return x; as the last line |
x |
ptr_arg |
fn f(v: &Vec<T>), fn g(s: &String) |
fn f(v: &[T]), fn g(s: &str) |
manual_map |
match o { Some(x) => Some(f(x)), None => None } |
o.map(f) |
useless_format |
format!("{}", s) |
s.to_string() |
bool_comparison |
if done == true |
if done |
unwrap_or_default |
.unwrap_or(String::new()) |
.unwrap_or_default() |
single_match |
match o { Some(x) => ..., _ => {} } |
if let Some(x) = o { ... } |
collapsible_if |
if a { if b { ... } } |
if a && b { ... } |
Why &[T] over &Vec<T>? A slice parameter accepts a Vec, an array, or part of either; &Vec<T> only accepts a whole Vec, and adds an extra pointer hop. Same for &str over &String.
Here's a function with four of those warnings, and its clippy-clean version:
fn total(prices: &Vec<u32>) -> u32 {
if prices.len() == 0 {
return 0;
}
let mut sum = 0;
for i in 0..prices.len() {
sum += prices[i];
}
return sum;
}
fn total(prices: &[u32]) -> u32 {
prices.iter().sum()
}
fn main() {
let cart = vec![1999, 450, 300];
println!("{}", total(&cart));
println!("{}", total(&[]));
println!("{}", total(&cart[1..]));
}
2749
0
750
The empty check disappeared too: the sum of nothing is already 0. And total(&cart[1..]) works only because the parameter is a slice.
(This site compiles with rustc, not clippy, so its clippy exercises use rules that reject the flagged patterns.)
Common mistakes
- Silencing instead of fixing:
#[allow(clippy::...)]has its place (next step), but most warnings point at a genuinely better way. - Changing behavior while "just" fixing lints: rewrites like these should keep the output identical. Run the tests after each fix.
- Ignoring clippy until there are 300 warnings. Run it often; it's much easier one warning at a time.
Your task
Rewrite total_weight so clippy has nothing to say: take a slice instead of &Vec, don't compare len() with 0, don't loop over indexes, and don't end with return. It must still return the same totals.
Challenge 2
Clippy flags display_name with unwrap_or_default, bool_comparison (== true) and useless_format (format!("{}", x)), and a reviewer adds two more: take &str instead of &String (what ptr_arg asks for), and make the body an if/else expression instead of using an early return. Rewrite it with all five fixes. It shows nick (full name), or just the full name when there's no nickname or the nickname is empty.
Challenge 3
Clippy flags this loop with needless_range_loop: i is only used for indexing. Rewrite it with iterators (zip to walk both arrays together and enumerate for the position) so it prints exactly the same:
1. ana: 30
2. bo: 45
3. cy: 12
Previous: Documenting code with rustdoc Next: Configuring clippy and rustfmt