0.11Warnings, lints, and Clippy
So far we've talked about compiler errors: your program breaks a rule, the compiler explains, no executable is produced. But the compiler has a second, gentler voice. A warning means: this code is legal, I compiled it, and I think something about it deserves your attention.
Here's a program that earns one:
fn main() {
let unused = 5;
println!("Hello!");
}
This compiles and runs fine. But building it gets you:
Compiling warn_demo v0.1.0 (/Users/you/projects/warn_demo)
warning: unused variable: `unused`
--> src/main.rs:2:9
|
2 | let unused = 5;
| ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused`
|
= note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
warning: `warn_demo` (bin "warn_demo") generated 1 warning (run `cargo fix --bin "warn_demo" -p warn_demo` to apply 1 suggestion)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.11s
Look at the anatomy, because every Rust warning is shaped like this: the warning: line states the concern, the arrow points into your source at the exact spot, a help: note proposes a concrete fix, and the note: names the rule producing it. Then the build finishes anyway; warnings never stop compilation. The proposed fix here (renaming the variable to _unused) is a real convention: a leading underscore tells both the compiler and human readers "yes, this is deliberately unused," and the warning stands down.
Why warn at all over something legal? Because legal-but-pointless code is usually a symptom: a variable you meant to use but didn't is the visible half of a bug, the way one shoe by the roadside implies a story. The compiler can't know your intent, so it compiles the code and raises an eyebrow.
Key insight
Errors are for code that breaks the rules. Warnings are for code that follows the rules while looking like a mistake. The compiler resolves the ambiguity by doing its job and telling you its doubts, which is exactly what you'd want from a sharp colleague.
If you've programmed C or C++ before, notice what you did not have to do: turn the warnings on. There, meaningful diagnostics hide behind flags every beginner has to be told about (-Wall and friends, a lesson-length topic on learncpp.com, this course's C++ counterpart). Rust ships with its warnings on and its defaults sane, and the culture treats them as signal. The tutorials, the tools, and the libraries all assume a warning is something you handle.
Best practice
Deal with warnings as they appear: fix the cause, or (where the code is truly intentional) silence that one instance deliberately, underscore-style. The reason is practical: a build that's normally silent makes every new warning impossible to miss, while a build with forty stale warnings hides the one that matters. Warning number forty-one is invisible.
For advanced readers
Teams often go further and make warnings fail the build, typically in their continuous-integration checks (the robots that build every proposed change). Rust can do that with a flag or a one-line attribute. The trade-off to know before copying that idea into your own projects: new compiler releases occasionally introduce new warnings, so "warnings are fatal" means a compiler update can break a build that worked yesterday. Standard practice is strict robots, forgiving laptops.
Clippy: the warnings connoisseur
The compiler's built-in warnings are the conservative tier. Rust also ships Clippy, a separate analyzer with hundreds of additional lints: checks for code that's legal, often even sensible-looking, but unidiomatic, needlessly slow, or subtly error-prone. Run it like this, on any project:
cargo clippy
Give it something to disapprove of:
fn main() {
let on = true;
if on == true {
println!("running");
}
} Checking clippy_demo v0.1.0 (/Users/you/projects/clippy_demo)
warning: equality checks against true are unnecessary
--> src/main.rs:3:8
|
3 | if on == true {
| ^^^^^^^^^^ help: try: `on`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.96.0/index.html#bool_comparison
= note: `#[warn(clippy::bool_comparison)]` on by default
warning: `clippy_demo` (bin "clippy_demo") generated 1 warning (run `cargo clippy --fix --bin "clippy_demo" -p clippy_demo -- ` to apply 1 suggestion)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.42s
Clippy's point: comparing a boolean to true is redundant; if on already does that (a topic for chapter 4, but the gist is visible). Notice the output format is identical to the compiler's warnings: same anatomy, same suggested-fix machinery, plus a documentation link explaining the lint in depth. Clippy is the same voice with stronger opinions.
While you're learning, Clippy is close to a free tutor: it catches beginner idioms and explains the better form, with that documentation link attached to every lint. Running it now and then on your practice programs will teach you real Rust style faster than any list of rules.
rustfmt: the end of formatting debates
One more bundled tool completes the set. rustfmt automatically formats your code (indentation, spacing, line breaks) to the single standard style the Rust community settled on. Run cargo fmt and your whole project snaps to it. We'll have more to say in chapter 1 when we cover code formatting; for now, know that the tool exists, everyone uses it, and entire decades-old arguments about where braces belong do not occur in Rust. It's quietly one of the language's best features.
One lesson left in this chapter: editions, the answer to a question that's been sitting unexplained in your Cargo.toml since lesson 0.8.