Macros

macro_rules!

The macro_rules! construct defines a declarative macro with one or more pattern-matching arms, supporting overloaded arities, custom keyword syntax, and recursive self-calls.

Multi-Arm Overloading in macro_rules!

Because Rust functions do not support arity overloading, macro_rules! is used whenever a construct should accept 0, 1, 2, or $N$ arguments with distinct expansions per arm:

macro_rules! ensure {
    // Arm 1: Condition only (uses default message)
    ($cond:expr) => {
        if !$cond {
            return Err(format!("assertion failed: `{}`", stringify!($cond)));
        }
    };
    // Arm 2: Condition + custom error message
    ($cond:expr, $msg:expr) => {
        if !$cond {
            return Err(String::from($msg));
        }
    };
}

fn transfer(balance: i64, amount: i64) -> Result<i64, String> {
    ensure!(amount > 0);
    ensure!(balance >= amount, "insufficient funds");
    Ok(balance - amount)
}

fn main() {
    println!("Valid:    {:?}", transfer(100, 40));
    println!("Zero amt: {:?}", transfer(100, 0));
    println!("Overdraw: {:?}", transfer(100, 250));
}
Valid:    Ok(60)
Zero amt: Err("assertion failed: `amount > 0`")
Overdraw: Err("insufficient funds")

Wrapping Multi-Statement Expansions in a Block { ... }

When a macro arm expands into multiple statements (or declares temporary local variables) and is used in expression position, wrap the transcriber body inside an extra block { ... } so the temporary bindings stay scoped to the macro expansion and the block evaluates to a single expression:

macro_rules! timed_eval {
    ($label:expr, $body:expr) => {{
        // Extra `{ ... }` block scopes `result` and returns it as the block's value:
        let result = $body;
        println!("[{}] evaluated to {:?}", $label, result);
        result
    }};
}

fn main() {
    let sum = timed_eval!("addition", 15 + 27);
    println!("Final sum = {sum}");
}
[addition] evaluated to 42
Final sum = 42

Rule Ordering: Specific Arms Before General Arms

Because macro_rules! evaluates arms strictly from top to bottom and commits to the first matching arm, always place more specific literal or multi-token rules above general catch-all rules (such as ($x:expr)):

macro_rules! classify {
    // Specific literal rule MUST come first:
    (0) => {
        "zero"
    };
    // General expression rule comes second:
    ($e:expr) => {
        if $e > 0 { "positive" } else { "negative" }
    };
}

fn main() {
    println!("0  -> {}", classify!(0));
    println!("42 -> {}", classify!(42));
    println!("-7 -> {}", classify!(-7));
}
0  -> zero
42 -> positive
-7 -> negative