🎓 A1Learner
🔍

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?