6.1. Attribute syntax
This section defines the lexical and syntactic form of attributes and their attachment rules. Attributes follow this grammar:
attribute ::= outer_attr | inner_attr
outer_attr ::= "#" "[" attr_body "]"
inner_attr ::= "#" "!" "[" attr_body "]"
attr_body ::= path attr_args?
attr_args ::= "(" attr_arg ("," attr_arg)* ","? ")"
| "=" literal
attr_arg ::= literal | identifier | attr_body | identifier "=" literal
6.1.1. Introducers
Attributes are introduced by two tokens defined in punctuation and operator tokens.
#[...] outer attributes
An outer attribute is attached to the item that immediately follows it. For example:
#[derive(Debug)]
struct Point { x: f64; y: f64; }
#[inline(always)]
fn fast() { ... }
#[...] attaches to the item that follows.
#![...] inner attributes
An inner attribute is attached to the item or module that encloses it:
#![cfg(target_os = "linux")] // applies to this whole module / file
mod m {
#![allow(unused)] // applies to module m itself
fn helper() { ... }
}
Inner attributes appear at the start of a module, file, or function body. The outer/inner distinction follows the same pattern as /// (outer doc comment) and //! (inner doc comment) in lexical structure.
6.1.2. Body forms
An attribute body has three forms: #[name], #[name(args)], and #[name = value].
Bare name
The simplest form uses only a name:
#[test]
#[inline]
#[derive_attr(default)]
The bare-name form is used as a boolean flag or marker.
With arguments
The form with arguments is also common:
#[derive(Debug, Clone, PartialEq)]
#[repr(C)]
#[link("m")]
#[inline(always)]
#[cfg(target_os = "linux")]
#[infix("+")]
This is the name(args) form, with arguments separated by commas. Each argument may be an identifier, a literal, a key-value (like = "+"), or a nested attribute form (Display inside derive(...)).
Key-value
Finally there is the key-value form:
#[doc = "This function ..."]
#[link_name = "external_symbol"]
This is the name = value form, where the value is a literal. It looks the same as the key-value notation within the argument form, except that the whole body is a single key-value.
6.1.3. Items that may carry attributes
Attributes may be attached to the following:
| Item | Example |
|---|---|
| Function / method | #[inline] fn f() { ... } |
struct / enum / union | #[derive(PartialEq, Eq)] struct S { ... } (derive on a union is an error) |
trait / impl | #[deprecated] trait T { ... } |
const / static | #[deprecated] const X: i32 = 0; |
| Module | #[cfg(test)] mod tests { ... } |
| Fields / variants within an item (including tuple-variant fields) | struct S { #[deprecated] x: i32; }, enum E { V(#[cfg(x)] i32) } |
| Trait methods / associated items | #[infix("+")] fn add(...); |
use / extern | #[link("m")] extern "C" { ... } |
Statements (only #[cfg]) | #[cfg(test)] my_test_call(); |
Match arms (only #[cfg]) | #[cfg(debug_assertions)] Op::Trace => trace(), |
Struct literal fields (only #[cfg]) | S { #[cfg(x)] extra: 1, .. } |
Function parameters (only #[cfg]) | fn f(#[cfg(x)] extra: i32) { ... } |
Which attributes are meaningful in each position depends on each attribute's definition; for example, #[derive] applies only to types and #[inline] only to functions.
6.1.4. Multiple attachment
Several attributes may be attached to the same item.
#[derive(Debug, Clone)]
#[repr(C)]
#[deprecated]
struct OldPoint { x: f64; y: f64; }
Each #[...] may be on its own line, or several may share a line for readability.
#[inline] #[cold] fn rarely_called() { ... }
6.1.5. When attributes take effect
When an attribute takes effect varies by attribute.
- Compiler-recognized attributes (e.g.
#[inline],#[derive]) are processed directly by the compiler at compile time. - Magic attributes (e.g.
#[lang],#[literal],#[infix]) are processed by the compiler in combination with language meaning. - User-defined attributes (from meta blocks) generate code in the template expansion pass.
All such processing completes in the meta-expansion phase right after parsing. The detailed flow is defined in template tokens and expansion.
6.1.6. Unknown attributes
The compiler may encounter an attribute it does not recognize:
#[mystery_attribute]
fn f() { ... }
An attribute that is neither built in nor defined or tested by any meta block (including @field.has_attr(x)) is a compile error, so typos are never silently ignored. Built-in attributes are checked for placement and argument shape.
6.1.7. Attributes and hygiene
When an attribute attached to an item leads to code generation, identifiers in the generated code follow the hygiene rules of 6. Meta-system, so they do not collide with the user code's name space. The detailed hygiene rules are defined in template tokens and expansion.
6.1.8. Multiple mappings of the same token
Several attributes may be attached to the same method:
#[infix("≤")]
#[infix("<=")]
fn le(self, rhs: Self) -> bool;
Both tokens (≤, <=) then dispatch to the same method. This pattern is often used to tie together Unicode and ASCII tokens with the same meaning.
6.1.9. Interaction of attributes with visibility and other keywords
Attributes may be combined in any order with visibility keywords (pub, pub(pkg), etc.) and function qualifiers (async, yield, etc.).
#[inline]
pub fn f() { ... }
pub
#[inline]
fn g() { ... } // same meaning
pub async fn h() { ... }
#[inline] pub async fn h() { ... }
pub #[inline] async fn h() { ... }
Idiomatically the order is attribute → visibility → qualifier (#[inline] pub async fn ...), but the specification does not enforce an order.