On this page
Macros
Zolo expands macros before type checking and lowering. Use a macro when syntax must be generated or inspected; use a function when ordinary values are enough.
Two declarative forms are available:
macro name(args) { ... }is the compact form for expression substitution.macro_rules! name { ... }adds pattern arms, capture kinds, and repetition.
Both forms rename bindings introduced by an expansion so they do not overwrite same-named bindings at the call site.
Compact macro¶
Parameters are referenced with $name inside the macro body. Invoke the macro
with name!(arguments):
macro double(value) {
$value + $value
}
let result = double!(21)
print(result)Output:
42Substitution is syntactic. If an argument appears twice in the body, its expression is evaluated twice after expansion. Bind a value before invoking the macro when duplicate evaluation would be observable.
macro_rules!¶
macro_rules! tests its arms from top to bottom and expands the first complete
match. Captures use $name:kind; supported kinds include expr, ident,
ty, lit, pat, block, and tt.
Repetition uses $( ... )* or $( ... )+, with an optional separator before
the repetition operator. This example accepts any number of comma-separated
expressions:
macro_rules! sum {
($($value:expr),*) => {{
var total = 0
$( total = total + $value; )*
total
}}
}
print(sum!{1, 2, 3, 4})
print(sum!{10, 20})Output:
10
30Calls may use braces, brackets, or parentheses. Braces are the clearest choice
for macro_rules! because parentheses are also used by the compact macro form.
Hygiene¶
Bindings created by the expansion receive a unique internal name. Captured
identifiers keep the identity of the caller's syntax. You can therefore use a
scratch name such as total inside a macro without overwriting a caller's
total.
Hygiene is lexical, not a type-system feature. Expansion still happens before the type checker, so invalid generated code is reported only after the macro has expanded.
Dispatch and recursion¶
Several arms can cover different shapes or arities. If no arm matches the
entire token stream, expansion fails with a no matching rule error.
Macros can invoke other macros and can recurse. Expansion depth is capped at 64 so an accidental infinite recursion fails during compilation.
Current limits¶
- Procedural
@proc_macrodeclarations are scaffold syntax only. Their bodies are not executed to transform code, so do not use them as a public feature. - The declarative matcher is greedy and does not implement Rust's full backtracking behavior for deeply nested repetitions.
- Diagnostics point at the expanded syntax in some cross-file cases; complete source-context preservation is still pending.
Learn more¶
- Macros by example covers the
compact form, hygiene, recursion, block arguments, and
macro_rules!. - Executable macro examples are the runnable source behind the tour.