Lint Levels & Attributes
Rust lints can be allowed, warned, denied, or forbidden at crate, module, or item level. Cargo.toml [lints] tables centralize policy for rustc and clippy.
Recipe
[lints.rust]
unsafe_code = "forbid"
missing_docs = "warn"
[lints.clippy]
all = "warn"
pedantic = "allow"#[deny(clippy::unwrap_used)]
fn production_path() { }When to reach for this: Crate-wide safety policy, tightening warnings-as-errors, or targeted exceptions.
Working Example
#![deny(unsafe_code)]
#![warn(missing_docs)]
mod api {
#![allow(clippy::module_name_repetitions)]
/// Creates a user.
pub fn create_user() { }
}# Cargo.toml alternative to inner attributes
[lints.rust]
unsafe_code = "forbid"What this demonstrates:
- Crate attributes apply to entire compilation unit
- Inner
#![...]on modules scope subtree forbidcannot be overridden downstream (strongest)- Manifest
[lints]preferred for workspace consistency
Deep Dive
Levels
| Level | Behavior |
|---|---|
allow | Silent |
warn | Warning, build succeeds |
deny | Error |
forbid | Error; cannot allow in children |
rustc vs clippy
[lints.rust]
unused_must_use = "deny"
[lints.clippy]
unwrap_used = "deny" # apps: deny; libs: warn- Libraries often
warnonunwrapin public API - Applications may
denyunwrap_usedin non-test code
Gotchas
#![deny(warnings)]without policy - breaks on new Rust warnings after upgrade. Fix: pin toolchain; fix warnings promptly on bump.forbidthen allow in submodule - compile error. Fix: usedenyif children need exceptions.- Duplicate attributes and manifest - confusing precedence. Fix: pick one source of truth per lint.
- Allowing
clippy::all- disables valuable checks. Fix: allow specific lints only. - Test code same lint level -
unwrapin tests is fine. Fix:#[cfg(test)] mod tests { #![allow(clippy::unwrap_used)] }.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
CI -D warnings only | No manifest support needed | Per-crate policy differences |
cargo clippy -- -W lint | Experiment locally | Permanent policy |
#[expect(lint)] (nightly) | Suppress with intent tracking | Stable MSRV 1.97 teams use allow |
FAQs
deny(warnings) in lib vs bin?
Libs often warn; bins/services deny for production rigor.
How lint inheritance works in workspace?
Each crate has own [lints]; share via copy or workspace lint table future patterns - document in CONTRIBUTING.
missing_docs on private items?
Usually public items only; configure missing_docs on pub via clippy/rustc docs.
Can CI override manifest?
RUSTFLAGS and clippy args add warnings; manifest still applies as base.
forbid unsafe in workspace?
Set on domain crates; allow single ffi crate with documented safety reviews.
How allow for one line?
#[allow(clippy::lint)] on fn or statement.
What breaks on edition bump?
New rustc lints may warn; run cargo fix and clippy after toolchain upgrade.
deny in build.rs?
Separate compilation unit; set lints in build.rs crate attributes if needed.
Clippy lint groups?
clippy::all, clippy::pedantic - enable groups then allow noisy subsets.
Document lint policy?
ARCHITECTURE.md or CONTRIBUTING: unsafe, unwrap, missing_docs expectations.
Related
- Clippy - clippy-specific lints
- Documentation Lints - missing_docs
- Configuring Clippy - thresholds
- Linting in CI -
-D warnings
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.