Short answer
The error is appearing because unused_variables — normally a warning — has been promoted to deny, so rustc aborts the build on let x = ...; that is never read. The fix is not to remove the lint, but to scope it correctly: keep unused_variables at deny (or warn) for your crate only, leave everything else at default levels, and use -D/-W flags — never -F — for temporary overrides.
Permanent per-crate configuration
The [lints] table in Cargo.toml (supported since Rust 1.74) is the cleanest way to make this durable and scoped to your crate:
[lints.rust]
unused_variables = "deny"
dead_code = "warn"
Lints configured this way apply only when compiling your crate. Dependencies are built with their own lint configuration, so a dependency that intentionally leaves a variable unused will still compile. The older equivalent — #![deny(unused_variables)] at the top of src/main.rs or src/lib.rs — behaves the same way and works on older toolchains.
Everything you do not list keeps its default level, so dead_code, unused_imports, and the rest still surface as warnings. That is how you keep the signal without silencing other diagnostics.
Temporary overrides without touching source
For one-off strictness — for example in CI or while cleaning up a module — forward the flag to rustc:
cargo clippy -- -D unused-variables
cargo clippy -- -W clippy::pedantic -D unused-variables
The -- separator matters: everything after it goes to rustc, not to cargo. This changes nothing in your source or Cargo.toml, so the override lasts only for that invocation. Because it applies to your crate's compilation (dependencies were already built with their own settings), it stays scoped to your code. Note that unused_variables is a rustc lint, not a Clippy lint, which is why it works through this forwarding mechanism.
Why deny, not forbid
forbid (-F) cannot be overridden anywhere in the crate, and promoting an unknown or renamed lint to a hard level is what breaks builds on toolchains where that lint does not exist. deny still produces an error but can be locally relaxed with #[allow(unused_variables)] on a specific item — useful for intentional cases like trait implementations or feature-gated code:
#[allow(unused_variables)]
fn handler(x: Request) { /* x used only under cfg(feature = "logging") */ }
Verify on your toolchain
rustc --version
rustc -W help | grep unused-variables
The lint name is stable and long-established, but confirm it exists on the oldest toolchain in your support matrix before committing the [lints] table. One caveat worth checking: the [lints] manifest table itself requires Cargo 1.74+; on older toolchains the table is ignored (or errors, depending on version), so use the crate-root attribute form there. If your crate is a published library, consider warn instead of deny — a hard error in a library can frustrate downstream builds that hit edge cases you did not.