liveBook · Manning

Macro transcriber

As explained in Write Powerful Rust Macros

A macro transcriber is the output-template portion of a declarative macro definition that specifies generated syntax for a matched invocation.

A macro transcriber is the output side of a Rust declarative macro rule. A rule pairs a matcher with a delimited transcriber: when an invocation matches, the transcriber’s code is generated at the invocation site. section 2.0 section 2.5

In `macro_rules!`, write rules as `(matcher) => (transcriber)`. Both sides require delimiters—parentheses, braces, or square brackets—and multiple rules are separated with semicolons. Parentheses are a practical default, while braces can obscure nested code blocks. section 2.1.1

Generating an empty vector

An empty matcher can select a transcriber containing `Vec::new()`. Calling `my_vec!()` is replaced with `Vec::new()`, while the surrounding statement, including its semicolon, remains in place. A literal matcher such as `(make an empty vec)` can generate the same output. section 2.1.3 section 2.1.4

A transcriber can reuse values captured by its matcher. For `{$x:expr}`, `$x` is inserted into generated code that creates a vector and pushes the supplied expression. Repetition can similarly emit one `push` call per matched expression. Prefer concrete fragment specifiers such as `expr` before using broad `tt` matches, and test broad matches carefully. section 2.1.4

Listing 2.1 my_vec, our first declarative macro
macro_rules! my_vec {      #1
    () => [             #2
        Vec::new()
    ];                   #3
    (make an empty vec) => (      #4
        Vec::new()
    );        #5
    {$x:expr} => {
        {
            let mut v = Vec::new();
            v.push($x);
            v
        }
    };  #6
    [$($x:expr),+] => (
        {
            let mut v = Vec::new();
            $(
                v.push($x);
            )+
            v
        }
    )             
}

fn main() {
    let empty: Vec<i32> = my_vec![];
    println!("{:?}", empty);                #7
    let also_empty: Vec<i32> = my_vec!(make an empty vec);
    println!("{:?}", also_empty);          
    let three_numbers = my_vec!(1, 2, 3);
    println!("{:?}", three_numbers);         #8
}

Inspect a complete `my_vec!` macro with empty, literal, single-expression, and repeated-expression matchers, each paired with generated vector-building code. section 2.1

Transcribers make declarative macros useful for removing boilerplate, expressing default arguments or varargs, and building DSL-like syntax. Captured input, recursive macro calls, and macro hygiene support larger generated-code patterns; procedural macros remain an option when declarative rules are insufficient. section 2.5

How Write Powerful Rust Macros covers it

How deep it goes
Explains rule delimiters, expansion, captured expressions, and repetition, while deferring more powerful cases to procedural macros.
Examples
Uses a simplified `my_vec!` implementation that expands empty, literal, single-expression, and repeated-expression invocations into vectors.
What it assumes
Assumes intermediate Rust familiarity while incrementally defining declarative macro rule syntax.
What you can do after
Apply it via `macro_rules!` to generate Rust expressions from matched macro input.
How it connects
Connects declarative macros with matchers, metavariables, token trees, repetition, recursion, hygiene, and procedural macros.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Write Powerful Rust Macros. The section links point to the parts of the book it draws on.