Macros
Macro Patterns
A macro pattern (the matcher on the left side of => in a macro_rules! arm) combines literal tokens, balanced delimiter groups ((), [], {}), and typed metavariables ($name:fragment) to parse custom domain-specific syntax at compile time.
Components of a Macro Matcher
- Metavariables (
$name:specifier): Capture a specific grammatical construct (such as$id:ident,$val:expr,$ty:ty) and bind it to$name. - Literal tokens and keywords: Any non-
$token in the matcher (such as=>,:,where,->, or custom words likefromandselect) must appear verbatim in the macro invocation. - Delimiter groups (
(...),[...],{...}): Match balanced parentheses, brackets, or braces in the input token stream.
Matcher:
( fn $name:ident ( $arg:ident : $arg_ty:ty ) -> $ret:ty => $body:expr )
^^ ^^^^^^^^^^^ ^ ^^^^^^^^^^ ^ ^^^^^^^^^^ ^ ^^ ^^^^^^^ ^^ ^^^^^^^^^^
lit metavar del metavar lit metavar del lit metavar lit metavar
macro_rules! mini_query {
(SELECT $field:ident FROM $source:expr WHERE $item:ident => $cond:expr) => {{
$source
.iter()
.filter(|$item| $cond)
.map(|$item| $item.$field)
.collect::<Vec<_>>()
}};
}
struct User {
name: &'static str,
age: u32,
}
fn main() {
let users = [
User { name: "Alice", age: 30 },
User { name: "Bob", age: 17 },
User { name: "Carol", age: 25 },
];
let adults = mini_query!(SELECT name FROM users WHERE u => u.age >= 18);
println!("Adult names: {adults:?}");
}
Adult names: ["Alice", "Carol"]
Follow-Set Restrictions to Prevent Parse Ambiguity
Because the Rust parser parses metavariables like $e:expr or $t:ty greedily in a single pass without backtracking, macro_rules! enforces follow-set rules on what tokens may immediately follow a metavariable:
| Fragment Specifier | Allowed Following Tokens in Matcher |
|---|---|
expr and stmt |
Only =>, , (comma), or ; (semicolon) |
ty and path |
=>, ,, ;, =, |, :, >, >>, [, {, as, where, or a block |
pat |
=>, ,, =, |, if, in |
ident, tt, literal, block |
Any token (never ambiguous) |
Attempting to place an arbitrary identifier or operator like + immediately after $a:expr fails at compile time because $a:expr would greedily consume + $b as part of the first expression:
// ❌ does not compile
macro_rules! bad_add {
($a:expr + $b:expr) => {
$a + $b
};
}
fn main() {}
error: `$a:expr` is followed by `+`, which is not allowed for `expr` fragments
--> src/main.rs:3:14
|
3 | ($a:expr + $b:expr) => {
| ^ not allowed after `expr` fragments
|
= note: allowed there are: `=>`, `,` or `;`