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