I have been using Rust on and off for the past five years or so, first picking it up around the time release 1.0 dropped. I then seriously learned the language in 2019, and, thanks to coding events such as Advent of Code, found a reason to make (arguably) beautiful code that trained me to make simple data structures work efficiently together to solve problems. Nowadays, I scale my development towards multi-threaded, asynchronous programs, in particular web server toys and active projects (to be announced soon).
My workflow in Rust has increasingly involved a tool that my peers find funny (for good reasons), and which has helped me tremendously when it came to writing Rust that works but also works well and is pleasant to read: pain.
Clippy is Rust's official linter program to "catch common mistakes and improve your Rust code". Rust is, contrary to what people feel, extremely friendly towards new developers: the compiler spits out nice error messages with suggestions for fixes that are almost always right, the Book is the greatest piece of documentation I have ever read, the community of developers is extremely friendly and ready to explain concepts that are indeed hard to grasp for a lot of developers at first, etc.
Clippy can be installed into
cargo, Rust's project management tool, using the rustup toolchain manager. It integrates perfectly in the environment of other tools cargo provides, and it can be integrated in your linting routine to show you common mistakes.
Clippy groups its linting rules into groups, or categories that you can find on their search engine and allow/deny/warn as a whole. The most notable groups are:
Other categories exist, but these are the ones I think about the most. Another one I have to mention is restriction, which is hilarious to me. It is meant to restrict usage of patterns forbidden for a specific project (either because of style policies, or constraints). In fact, it is so thorough and unforgiving that you literally cannot act on it as a whole, since some of the lints in there are contradictory (see the AT&T and Intel asm flavour lints, for example).
The seven groups I mentioned above, along with
clippy::cargo, are completely denied in most, if not all of my projects. They will typically also include more lints from standard Rust. As of the writing of this post, my linting header is as follows:
// Make clippy quite nasty #![deny(clippy::cargo)] // Checks for garbage in the Cargo TOML files #![deny(clippy::complexity)] // Checks for needlessly complex structures #![deny(clippy::correctness)] // Checks for common invalid usage and workarounds #![deny(clippy::nursery)] // Checks for things that are typically forgotten by learners #![deny(clippy::pedantic)] // Checks for mildly annoying comments it could make about your code #![deny(clippy::perf)] // Checks for inefficient ways to perform common tasks #![deny(clippy::style)] // Checks for inefficient styling of code #![deny(clippy::suspicious)] // Checks for potentially malicious behaviour // Add some new clippy lints #![deny(clippy::use_self)] // Checks for the use of a struct's name in its `impl` // Add some default lints #![warn(unused_variables)] // Checks for unused variables // Deny missing documentation #![deny(missing_docs)] #![deny(rustdoc::missing_crate_level_docs)]
This is pain incarnate. People I talk with who are starting out in Rust would rather bang their head against a wall than try and satisfy such draconian requirements. By all means, I tell them, they could be far worse!
Yet this approach forces you to write code that is not only valid, and correct, but good. I started out simply denying
clippy::pedantic, which is enough to get a good grasp of good practices in Rust. Once no lints typically fire after you are finished writing a piece of code, try and deny another category, like
perf. After a while, you will learn about the common mistakes you make, and recognize the patterns, and catch yourself in the act, before Clippy can yell at you. After that, learn about the mistakes you do from a new group!
At some point you end up with code that is functional, efficient and good-looking. Rust code already looks beautiful in general, but good Rust code is pleasant to read (especially if you literally force yourself to write good documentation with the documentation lints).
With time, you learn to love that nasty paperclip. 📎