Attributes and Conditional Compilation

Compiler Lints and Attributes

Compiler lints are static analysis checks performed by rustc, clippy, and rustdoc to catch bugs, performance pitfalls, unidiomatic patterns, and future edition incompatibilities. Lints can be configured at the crate, module, or item level via attributes (#[allow], #[expect], #[warn], #[deny], #[forbid]) or workspace-wide via the [lints] table in Cargo.toml.

Built-In Lint Groups in rustc

Instead of listing dozens of individual lint names, rustc groups related lints under collective lint group names:

Lint Group Included Lints / Purpose
warnings Matches every lint currently set to the warn level (#[deny(warnings)] turns all warnings into errors)
unused unused_variables, unused_imports, unused_mut, dead_code, unused_must_use, unused_assignments
nonstandard_style non_camel_case_types, non_snake_case, non_upper_case_globals
future_incompatible Lints warning about patterns that will stop compiling in a future Rust release or edition
rust_2018_idioms Explicit '_ lifetimes, extern crate cleanup
rust_2024_compatibility Lints preparing code for the Rust 2024 edition (unsafe_op_in_unsafe_fn, static_mut_refs, tail_expr_drop_order)

Centralizing Lint Configuration in Cargo.toml ([lints])

Stabilized in Rust 1.74, the [lints] table in Cargo.toml configures both rustc and clippy lints across all targets (lib, bin, tests, examples) without repeating #![warn(...)] headers in every root .rs file. In a multi-crate Cargo workspace, child crates inherit the workspace lints with [lints] workspace = true:

# Root Cargo.toml
[workspace.lints.rust]
unsafe_code = "forbid"
missing_debug_implementations = "warn"
unused_qualifications = "warn"

[workspace.lints.clippy]
all = { level = "warn", priority = -1 }
undocumented_unsafe_blocks = "deny"
dbg_macro = "warn"
todo = "warn"

# In member crate Cargo.toml:
[lints]
workspace = true

Summary of Core Compiler Attributes Across Categories

Category Attributes Primary Role
Conditional Compilation #[cfg(...)], #[cfg_attr(...)] Strips or attaches items/attributes based on target or feature flags
Derive & Codegen #[derive(...)], #[inline], #[cold], #[track_caller], #[target_feature] Generates trait impls, controls inlining/branch weights, and propagates caller locations
Memory Layout & FFI #[repr(...)], #[unsafe(no_mangle)], #[unsafe(export_name = "...")], #[unsafe(link_section = "...")] Controls struct/enum layout and linker symbol names
API Evolution & Safety #[must_use], #[non_exhaustive], #[deprecated] Enforces return-value inspection, extensible matching/construction, and migration notices
Diagnostics & Lints #[allow], #[expect], #[warn], #[deny], #[forbid], #[diagnostic::on_unimplemented] Tunes compiler/Clippy lints and customizes trait-bound error messages
Testing & Docs #[test], #[ignore], #[should_panic], #[doc(...)] Configures unit tests and rustdoc HTML generation

Customizing Trait Error Diagnostics with #[diagnostic::on_unimplemented]

Stabilized in Rust 1.78, the #[diagnostic::on_unimplemented(...)] attribute allows library authors to customize the exact compiler error message, label, and help notes shown when a caller passes a type T that fails to implement a trait:

#[diagnostic::on_unimplemented(
    message = "`{Self}` cannot be serialized as a database primary key",
    label = "this type is not a valid primary key",
    note = "only `u64`, `i64`, and `Uuid` implement `PrimaryKey`"
)]
pub trait PrimaryKey {
    fn key_bytes(&self) -> [u8; 8];
}

impl PrimaryKey for u64 {
    fn key_bytes(&self) -> [u8; 8] {
        self.to_be_bytes()
    }
}

fn lookup_by_key<K: PrimaryKey>(key: K) -> [u8; 8] {
    key.key_bytes()
}

fn main() {
    println!("key bytes: {:02x?}", lookup_by_key(42_u64));
}
key bytes: [00, 00, 00, 00, 00, 00, 00, 2a]