Rust Macro Designators
A designator is the word after the colon in a macro capture. It says what shape of code the capture accepts.
ident: a name the caller chooses
An ident capture can stand where a name is born — here it names a new function:
macro_rules! make_fn { ($name:ident) => { fn $name() {} }; }
make_fn!(wave);literal: literals only
A literal capture accepts a number, a piece of text, or true / false — nothing else:
macro_rules! only_num { ($l:literal) => { $l }; }
println!("n = {}", only_num!(42));tt: one token tree, even an operator
A tt has no follow-set limits. Capture the pieces as tt and an operator can travel between them — an expr capture would refuse this:
macro_rules! apply_op { ($a:tt $op:tt $b:tt) => { $a $op $b }; }
println!("sum = {}", apply_op!(2 + 3));vis: a visibility word, or none
vis captures pub, pub(crate), or nothing at all. The same macro captures the return type with :ty:
macro_rules! getter { ($v:vis fn $name:ident() -> $t:ty) => { $v fn $name() -> $t { Default::default() } }; }
getter!(pub fn answer() -> i32);
println!("answer = {}", answer());An expression capture must end its phrase
The first macro is legal. The second is refused when it is defined. The error is shown:
// Allowed: the capture is followed by =>
($e:expr) => { $e };
// Error: `$e:expr` is followed by `+`, which is not allowed for `expr` fragments
($e:expr + $f:expr) => { $e + $f };Example
macro_rules! make_fn {
($name:ident) => {
fn $name() {
println!("You called {}()", stringify!($name));
}
};
}
macro_rules! show_result {
($e:expr) => {
println!("{} = {}", stringify!($e), $e);
};
}
make_fn!(wave);
make_fn!(jump);
fn main() {
wave();
jump();
show_result!(2 + 3);
show_result!({
let x = 4;
x * x
});
}Key points
- Designator: the word after the colon in $name:kind — it says what shape of code this capture accepts
- The Reference adds two more: meta for the inside of an attribute, and lifetime for a name like 'a
- Follow set limits what comes next.
- Some captures limit the next token in the matcher.
- After $e:expr, only three tokens may follow.
- Only => , or ; may follow
- $s:stmt shares the small follow set of expr.
- $t:ty and $p:path allow more followers.
- Their followers include = | ; : and >
- Pick the narrowest designator that fits.
In this example, make_fn takes one $name:ident. Each call writes a real function with that name: wave and jump. Inside each function, stringify!($name) turns the captured name into text, so wave prints You called wave() and jump prints You called jump(). show_result takes one $e:expr instead. It prints the expression as text with stringify!, then runs the same expression: 2 + 3 = 5. The last call passes a whole block as the expression. A block is an expression. So it prints { let x = 4; x * x } = 16.
Note
Asking for expr when you only need a literal lets callers pass shapes your expansion never planned for.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
Which designator captures a name the caller chooses, so the macro can create a function with that name?