Macros generate Rust code at compile time. Macros by example, written with
macro_rules!, are defined by pattern matching on tokens. Procedural macros
are functions that run in the compiler and work on token streams (Reference: Macros,
Macros by example, Procedural macros).
Invocation
A macro is invoked by its path, ! and a delimited token tree, in any of
the three delimiters: m!(…), m![…] or m!{…}. An
invocation may appear as an expression, a statement, an item, a type, a pattern, and an
item in a trait, impl or extern block. Invocations in statement or item
position that use parentheses or square brackets end in a semicolon. Derive and
attribute macros are invoked as attributes.
macro_rules
MacroRulesDefinition:
macro_rules ! IDENTIFIER MacroRulesDef
MacroRulesDef:
( MacroRules ) ;
| [ MacroRules ] ;
| { MacroRules }
MacroRules: MacroRule ( ; MacroRule )* ;?
MacroRule: MacroMatcher => MacroTranscriber
MacroMatcher: ( MacroMatch* ) | [ MacroMatch* ] | { MacroMatch* }
MacroMatch:
Token except $ and delimiters
| MacroMatcher
| $ ( IDENTIFIER_OR_KEYWORD except crate | RAW_IDENTIFIER ) : MacroFragSpec
| $ ( MacroMatch+ ) MacroRepSep? MacroRepOp
MacroRepOp: * | + | ?
MacroTranscriber: DelimTokenTree
macro_rules! my_vec {
() => { Vec::new() };
( $( $x:expr ),+ $(,)? ) => {{
let mut v = Vec::new();
$( v.push($x); )+
v
}};
}
let v = my_vec![1, 2, 3];
A rule has a matcher and a transcriber. The expander tries the rules
in order and transcribes the first that matches. If that transcription produces a
compile error, later rules are not tried. The outer delimiters of a matcher match
any pair of delimiters. $ cannot be matched or transcribed literally.
Matching has no lookahead. In $($i:ident)* $j:ident the parser cannot tell
where the repetition ends, and it reports a local ambiguity.
Fragment specifiers
| Specifier | Matches |
|---|---|
block | A block expression |
expr | An expression. Before edition 2024 this does not match _ or a const block at the top level |
expr_2021 | An expression, except _ and const blocks |
ident | An identifier or keyword, except _, a raw identifier, or $crate |
item | An item |
lifetime | A lifetime token |
literal | An optional - and a literal |
meta | The contents of an attribute |
pat | A pattern, including top-level or-patterns from edition 2021. In earlier editions it is the same as pat_param |
pat_param | A pattern without a top-level or |
path | A type-style path |
stmt | A statement, without its trailing semicolon |
tt | A single token tree |
ty | A type |
vis | A possibly empty visibility qualifier |
Which edition applies is the edition of the crate where the
macro_rules! is written. In edition 2024, a metavariable with no
fragment specifier in the matcher of an unused rule is a hard error.
Repetitions
Written $( … ), then an optional separator, then one of the operators
* (zero or more), + (one or more) or ? (zero or
one, which cannot have a separator). The separator can be any token except a delimiter
or a repetition operator.
- A metavariable must be transcribed with the same number, kind and nesting of
repetitions as in the matcher.
{ $( $i );* }is valid for$( $i:ident ),*, but{ $i }is not. - Each repetition in the transcriber must contain at least one metavariable, and the metavariables in one repetition must have matched the same number of fragments.
Follow-set restrictions
To keep room for future syntax, certain fragments may be followed only by certain tokens (Reference: Macros by example).
| Specifier | May be followed only by |
|---|---|
expr, stmt | => , ; |
pat_param | => , = | if in |
pat | => , = if in (before edition 2021, also |) |
path, ty | => , = | ; : > >> [ { as where, or a block metavariable |
vis | ,, an identifier other than a non-raw priv, any token that can begin a type, or a metavariable with an ident, ty or path specifier |
| All others | Any token |
Forwarding a matched fragment
When a fragment is passed to another macro, the second macro sees an opaque
syntax node. It can only match it with the same specifier, not with literal tokens.
ident, lifetime and tt are the exceptions.
Scoping
A macro has two kinds of scope.
- Textual scope is based on source order, like a
letbinding but also at module level. A macro is in scope from its definition until the enclosing scope closes, and this extends into child modules, including in other files. Redefining a macro is not an error: the latest definition shadows the earlier one while in scope. - Path-based scope works like other items and needs a
#[macro_export]or ausere-export such aspub(crate) use m;.
An unqualified m!() checks textual scope first, then path-based scope. A
qualified path checks only path-based scope.
| Attribute | Effect |
|---|---|
#[macro_use] on a module | Extends the module’s macros past its closing brace |
#[macro_use] on extern crate | Imports exported macros into the macro_use prelude. #[macro_use(name)] imports only those named. Not allowed on extern crate self |
#[macro_export] | Exports the macro from the crate root, so crate::m!() and my_crate::m!() work. #[macro_export(local_inner_macros)] adds an implicit $crate:: to single-segment macro invocations in the definition |
Hygiene
Macros by example use mixed-site hygiene: local variables and loop or block labels are resolved where the macro is defined, and everything else (functions, types, other macros) is resolved where it is invoked.
let x = 1;
fn func() { unreachable!("this is never called") }
macro_rules! check {
() => {
assert_eq!(x, 1); // the definition-site x
func(); // the invocation-site func
};
}
{
let x = 2;
fn func() { /* does not panic */ }
check!(); // passes
}
A local variable or label introduced by one expansion is not visible to another expansion.
$crate refers to the crate that defined the macro, so a macro can reach
items in its own crate from anywhere: $crate::inner::foo(). It does not
change visibility: a call to a private item still fails.
Procedural macros
A procedural macro is a function that runs at compile time, in a crate of type
proc-macro (with Cargo, [lib] proc-macro = true). It must be
defined in the crate root and cannot be used in the crate that defines it. It works on
proc_macro::TokenStream values, and has the same security implications
as a build script: a procedural macro runs arbitrary code with the compiler’s access.
| Kind | Attribute | Signature | Used as |
|---|---|---|---|
| Function-like | #[proc_macro] | fn(TokenStream) -> TokenStream | my_macro!(…) |
| Derive | #[proc_macro_derive(Name, attributes(helper))] | fn(TokenStream) -> TokenStream | #[derive(Name)] |
| Attribute | #[proc_macro_attribute] | fn(TokenStream, TokenStream) -> TokenStream | #[my_attr] or #[my_attr(args)] |
use proc_macro::TokenStream;
#[proc_macro]
pub fn make_answer(_item: TokenStream) -> TokenStream {
"fn answer() -> u32 { 42 }".parse().unwrap()
}
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
TokenStream::new()
}
#[proc_macro_attribute]
pub fn return_as_is(_attr: TokenStream, item: TokenStream) -> TokenStream {
item
}
- Function-like macros take the tokens inside the delimiters and replace the whole invocation with the result.
- Derive macros take the item’s tokens and add new items after it. A derive macro can
declare inert helper attributes in
attributes(…)for use on the item’s fields and variants. - Attribute macros take the tokens of their argument and of the item, and replace the item with the output.
- Procedural macros are not hygienic: output behaves as if written
inline. Use absolute paths such as
::std::option::Option. - A macro reports errors by panicking or by producing a
compile_error!invocation.
The tokens differ slightly from those of macro_rules. Multi-character
operators arrive as single characters, lifetimes arrive as ' and an
identifier, negative literals can be a single token, and doc comments have already
been turned into #[doc = "…"] attributes.
Built-in macros
These macros come with the standard library or the compiler (std macro
list).
| Purpose | Macros |
|---|---|
| Printing | print! println! eprint! eprintln! dbg! |
| Formatting | format! format_args! write! writeln! |
| Assertions | assert! assert_eq! assert_ne! assert_matches! debug_assert! debug_assert_eq! debug_assert_ne! debug_assert_matches! |
| Panicking | panic! todo! unimplemented! unreachable! |
| Conditional code | cfg! cfg_select! compile_error! |
| Collections and tests | vec! matches! |
| Compile-time information | file! line! column! module_path! stringify! concat! env! option_env! |
| Including files | include! include_str! include_bytes! |
| Thread locals | thread_local! |
| Platform | is_x86_feature_detected! |
The format! family takes a format string, checked at compile time. A
replacement field is written {}, and can name its argument and give
options after a colon.
| Form | Meaning |
|---|---|
{} | Next argument, using Display |
{:?} | Next argument, using Debug |
{:#?} | Pretty-printed Debug |
{0}, {name} | A positional argument, a named argument, or a variable captured from the scope |
{:>8}, {:<8}, {:^8} | Width 8, right, left or centre aligned |
{:08.3} | Zero padding, width 8, three decimal places |
{:x} {:X} {:o} {:b} {:e} | Hexadecimal, octal, binary and scientific formats |
{{, }} | Literal braces |