#048 Mar 29, 2026

48. #[expect] — Lint Suppression That Cleans Up After Itself

Silencing a lint with #[allow] is easy — but forgetting to remove it when the code changes is even easier. #[expect] suppresses a lint and warns you when it’s no longer needed.

The problem with #[allow]

You add #[allow(unused_variables)] during a refactor, the code ships, months pass, the variable gets used — and the stale #[allow] stays forever:

1
2
3
4
5
#[allow(unused_variables)]
let connection = db.connect(); // was unused during refactor
// ... later someone adds:
connection.execute("SELECT 1");
// the #[allow] above is now pointless, but no one notices

Over time, your codebase collects these like dust. They suppress diagnostics for problems that no longer exist.

Enter #[expect]

Stabilized in Rust 1.81, #[expect] works exactly like #[allow] — but fires a warning when the lint it suppresses is never triggered:

1
2
3
4
5
6
#[expect(unused_variables)]
let unused = "Suppressed — and the compiler knows why";

#[expect(unused_variables)] // ⚠️ warning: this expectation is unfulfilled
let used = "I'm actually used!";
println!("{used}");

The first #[expect] is satisfied — the variable is unused, and the lint stays quiet. The second one triggers unfulfilled_lint_expectations because used isn’t actually unused. The compiler tells you: this suppression has no reason to exist.

Add a reason for future you

#[expect] supports an optional reason parameter that shows up in the warning message:

1
2
3
4
#[expect(dead_code, reason = "will be used once the API module lands")]
fn prepare_response() -> Vec<u8> {
    vec![0x00, 0xFF]
}

When prepare_response gets called and the expectation becomes unfulfilled, the warning includes your reason — so future-you (or a teammate) knows exactly why it was there and that it’s safe to remove.

Works with Clippy too

#[expect] isn’t limited to compiler lints — it works with Clippy:

1
2
3
4
#[expect(clippy::needless_return)]
fn legacy_style() -> i32 {
    return 42;
}

This is perfect for migrating a codebase to stricter Clippy rules incrementally. Suppress violations with #[expect], fix them over time, and the compiler will tell you when each suppression can go.

#[allow] vs #[expect] — when to use which

Use #[allow] when the suppression is permanent and intentional — you never want the lint to fire here. Use #[expect] when the suppression is temporary or when you want a reminder to revisit it. Think of #[expect] as a // TODO that the compiler actually enforces.

← Previous 47. Vec::pop_if — Conditionally Pop the Last Element