Attributes and Conditional Compilation

Inner and Outer Attributes

Rust distinguishes between two attribute placement forms based on the presence or absence of an exclamation mark !:

  • Outer attributes (#[...] — no !) apply to the item, field, variant, or expression that immediately follows the attribute.
  • Inner attributes (#![...] — with !) apply to the enclosing scope (the crate root file, module block, or function body) inside which the attribute is placed.
Outer Attribute (`#[...]` — applies to what FOLLOWS it):
  #[allow(dead_code)]      ---+
  struct Helper {             | <-- Applies only to `struct Helper`
      id: u32,                |
  }                        ---+

Inner Attribute (`#![...]` — applies to the ENCLOSING container):
  mod internal {           <--+
      #![allow(dead_code)] ---+ <-- Applies to the entire `mod internal` block!
      struct HelperA;
      struct HelperB;
  }

Comparing Inner (#![...]) and Outer (#[...]) Placement

Target Scope Inner Attribute Form (#![...]) Equivalent Outer Attribute Form (#[...])
Entire crate (src/lib.rs / src/main.rs) #![forbid(unsafe_code)] at top of root file None (the crate root has no enclosing file outside it!)
Inline module (mod foo { ... }) mod foo { #![allow(unused)] ... } #[allow(unused)] mod foo { ... }
File module (src/foo.rs) #![allow(unused)] at top of src/foo.rs #[allow(unused)] mod foo; in parent file
Function body (fn f() { ... }) fn f() { #![allow(unused_variables)] ... } #[allow(unused_variables)] fn f() { ... }
Struct / Enum / Field / Variant Not applicable #[derive(Debug)] struct S { #[serde(skip)] f: u8 }

Crate-Level Inner Attributes

Crate-wide configuration—such as #![no_std], #![forbid(unsafe_code)], #![warn(missing_docs)], and crate-level doc comments //!—can only be written as inner attributes at the very top of the crate root (src/lib.rs or src/main.rs), before any items are declared:

//! Crate-level inner doc comment (equivalent to `#![doc = "..."]`).
#![warn(unused_must_use)]

mod legacy_subsystem {
    // Inner attribute applying to everything inside `legacy_subsystem`:
    #![allow(dead_code)]

    pub struct ActiveRecord {
        pub id: u64,
    }

    // No warning emitted for `UnusedRecord` because of `#![allow(dead_code)]` above:
    struct UnusedRecord {
        tag: u8,
    }
}

// Outer attribute applying ONLY to `compute_checksum`:
#[inline]
fn compute_checksum(data: &[u8]) -> u32 {
    data.iter().map(|&b| u32::from(b)).sum()
}

fn main() {
    let rec = legacy_subsystem::ActiveRecord { id: 7 };
    println!(
        "record id = {}, checksum = {}",
        rec.id,
        compute_checksum(b"rust")
    );
}
record id = 7, checksum = 450

Doc Comments Desugar to Inner and Outer doc Attributes

Rust's doc comment syntax follows the exact same outer vs. inner convention:

  • /// ... (or /** ... */) is an outer doc comment that desugars to #[doc = "..."] on the following item.
  • //! ... (or /*! ... */) is an inner doc comment that desugars to #![doc = "..."] on the enclosing module or crate.