Linting your Rust code with Clippy
In Python you often use flake8, pyflakes and/or ruff to lint your code. In Rust, you can use Clippy.
Clippy is a collection of lints to catch common mistakes and improve your Rust code. Let's try it out on Pybites Search.
Installing and running Clippy
First make sure you install Clippy:
$ rustup component add clippy
Next you can invoke it in any project through Cargo:
$ cargo clippy
Running this in the Pybites search project we get:
โ search (main) $ cargo clippy
Checking pybites-search v0.6.0 (/Users/pybob/code/rust/search)
warning: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
--> src/main.rs:108:25
|
108 | fn save_to_cache(items: &Vec<Item>) -> Result<(), Box<dyn std::error::Error>> {
| ^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
= note: `#[warn(clippy::ptr_arg)]` on by default
help: change this to
|
108 ~ fn save_to_cache(items: &[Item]) -> Result<(), Box<dyn std::error::Error>> {
109 | let cache_path = get_cache_file_path();
110 | let cache_data = CacheData {
111 | timestamp: SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(),
112 ~ items: items.to_owned(),
|
warning: `pybites-search` (bin "psearch") generated 1 warning
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.16s
The warning is about using &Vec
instead of &[_]
. The fix is to change the function signature to fn save_to_cache(items: &[Item]) -> Result<(), Box<dyn std::error::Error>> {
(and items.clone()
to items.to_vec()
in the function body).
It's useful to check out the associated link where we can read about the why:
Requiring the argument to be of the specific size makes the function less useful for no benefit; slices in the form of &[T] or &str usually suffice and can be obtained from other types, too.
And a suggested fix:
fn foo(&Vec<u32>) { .. }
// use instead:
fn foo(&[u32]) { .. }
Note this is a warning, not an error, you can still compile and run your code.
But it's good practice to fix these warnings to improve the quality of your code. ๐ฆ ๐งน
Running Clippy on another project
Let's run it on the resize-images project:
warning: the borrowed expression implements the required traits
--> src/main.rs:54:105
|
54 | let output_path = Path::new(&output_dir).join(path.file_stem().unwrap()).with_extension(&extension);
| ^^^^^^^^^^ help: change this to: `extension`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrows_for_generic_args
= note: `#[warn(clippy::needless_borrows_for_generic_args)]` on by default
warning: `resize-images` (bin "resize-images") generated 1 warning (run `cargo clippy --fix --bin "resize-images"` to apply 1 suggestion)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 6.81s
This time it's about a needless borrow for generic args. The fix is to remove the &
from with_extension(&extension)
.
Again the help link explains why:
Suggests that the receiver of the expression borrows the expression.
And shows an example + fix:
fn f(_: impl AsRef<str>) {}
let x = "foo";
f(&x);
// use instead:
fn f(_: impl AsRef<str>) {}
let x = "foo";
f(x);
Auto-fixing Clippy warnings
You can also auto-fix Clippy warnings with cargo clippy --fix
. This will apply the suggestions it has for you. It's a great way to quickly clean up your code.
$ cargo clippy --fix
Running Clippy as part of pre-commit
You can run Clippy as part of your pre-commit hooks. This way you can't commit code with Clippy warnings.
If you're new to pre-commit, check out my video here
To do this, install the pre-commit tool and add the following to a .pre-commit-config.yaml
file in your project (taken from here):
repos:
- repo: https://github.com/doublify/pre-commit-rust
rev: v1.0
hooks:
- id: fmt
name: fmt
description: Format files with cargo fmt.
entry: cargo fmt
language: system
types: [rust]
args: ["--"]
- id: cargo-check
name: cargo check
description: Check the package for errors.
entry: cargo check
language: system
types: [rust]
pass_filenames: false
- id: clippy
name: clippy
description: Lint rust sources
entry: cargo clippy
language: system
args: ["--", "-D", "warnings"]
types: [rust]
pass_filenames: false
Apart from clippy, this also includes fmt
and cargo check
hooks.
Then install the pre-commit hooks:
$ pre-commit install
Now every time you commit code, Clippy + friends will run and you can't commit code with warnings. ๐ซ
To run it on all files retroactively in your project:
$ pre-commit run --all-files
I just did that and see here the result.
Imports are nicely ordered and the code is better formatted. This reminds me a lot of isort
and black
in Python, where Clippy is more like flake8
and pyflakes
๐ฆ ๐ ๐
Conclusion
Clippy is a great tool to help you write better Rust code. It's easy to install and run. You can even auto-fix warnings. ๐ช ๐
There are many more configuration options, check out the Clippy lints and its GitHub repo for more info.
It's also convenient to run it as part of pre-commit. This way you can't commit code with warnings. It's a great way to keep your code clean and readable. ๐ฆ ๐งน