Saltar al contenido

macro_rules!

En esta página

macro_rules! es la segunda forma de macro de Zolo, inspirada en Rust. La diferencia fundamental respecto a macro name(p) { ... } es que se escriben brazos de patrón — el expansor prueba cada brazo de arriba abajo y usa el primero que coincide. Esto habilita la sobrecarga de aridad, la repetición variádica y las restricciones de captura.

Forma básica

El brazo más simple no captura nada: () => { expresión }. La invocación usa llaves: name!{}:

greet!{} sin parámetros — la forma mínima de macro_rules!.

09-macro-rules-basic.zolo
Playground
// Feature: `macro_rules!` — pattern-matched macros (Rust-style)

// Syntax:

//   macro_rules! name {

//       () => { <expansion> }

//   }

// Invoked as `name!{...}`. Each `(...) => {...}` arm is a pattern;

// the engine tries them in order and picks the first match.

// When to use: short syntactic helpers where capture-by-position

// matters more than function-call semantics. Compare with the

// older `macro name(x) { ... }` form (files 01-08 in this folder)

// that captures by name.


macro_rules! greet {
    () => { "hello, world" }
}

print(greet!{})
// expected: hello, world

Repetición

El patrón $( $x:expr ),* captura cero o más expresiones separadas por coma. En el cuerpo, $( cuerpo )* se repite una vez por cada elemento capturado. Este es el mecanismo detrás de macros variádicos como sum!{a, b, c}:

sum! acumula cualquier cantidad de expresiones con $( ... ),*.

10-macro-rules-repetition.zolo
Playground
// Feature: repetition — `$( ... ),*` matches zero or more, comma-separated

// Syntax: inside the pattern, `$( $x:expr ),*` captures every comma-

// separated `expr`; inside the body, `$( body )*` repeats once per match.

// When to use: variadic macros — `sum!{a, b, c}`, `vec![1, 2, 3]`-style

// constructors, `concat!`-like builders.


macro_rules! sum {
    ($($x:expr),*) => {{
        var acc = 0
        $( acc = acc + $x; )*
        acc
    }}
}

print(sum!{1, 2, 3, 4})
// expected: 10


print(sum!{10, 20})
// expected: 30

Múltiples brazos y sobrecarga

Al listar varios brazos, se cubren diferentes aridades en el mismo macro. El expansor elige el primer brazo cuyo patrón encaje — comportamiento idéntico al de match:

describe! con cero, uno y dos argumentos — el primer brazo que coincide gana.

11-macro-rules-overload.zolo
Playground
// Feature: multiple arms — pick the first pattern that matches

// Syntax: list several `(...) => {...}` arms. The expander tries

// them top-to-bottom and uses the first one that fits.

// When to use: arity-overloaded macros — `assert!(cond)` vs.

// `assert!(cond, msg)`, default-argument convenience wrappers.


macro_rules! describe {
    () => { "nada" }
    ($x:expr) => { "um item" }
    ($x:expr, $y:expr) => { "dois itens" }
}

print(describe!{})
// expected: nada


print(describe!{42})
// expected: um item


print(describe!{1, 2})
// expected: dois itens

Higiene en macro_rules!

La misma garantía de higiene de la forma macro se aplica aquí: los bindings internos con let reciben un sufijo único y nunca sobreescriben los bindings del llamador:

double_sum! declara acc internamente sin afectar al acc del llamador.

12-macro-rules-hygiene.zolo
Playground
// Feature: hygiene under `macro_rules!` — internal vars never leak

// Syntax: any `let` bound inside the expansion gets a unique suffix,

// so the caller's `acc` is not overwritten when the macro also

// declares `acc`.

// When to use: any macro that needs scratch state. Hygiene is on by

// default; you don't have to do anything to opt in.


macro_rules! double_sum {
    ($x:expr) => {{
        let acc = $x
        acc + acc
    }}
}

let acc = 100
let result = double_sum!{5}

// The macro's internal `acc` did not overwrite the caller's `acc`.

print(acc)       // expected: 100

print(result)    // expected: 10

Tipos de captura

El sufijo después de : restringe el tipo de token aceptado en el patrón: :expr acepta cualquier expresión, :ident solo un identificador, :lit solo un literal (número, cadena, bool, nil). Esto permite errores de uso incorrecto más precisos en tiempo de expansión:

show_lit! con :lit y choose! con :expr ilustran las restricciones de captura.

13-macro-rules-capture-kinds.zolo
Playground
// Feature: capture kinds — `:expr`, `:ident`, `:lit`

// Syntax: in a pattern, `$name:kind` constrains what kind of token

// can match. `:expr` accepts any expression, `:ident` only an

// identifier, `:lit` only a literal (number, string, bool, nil).

// When to use: write tighter macros that catch misuse early. A

// `getter!(field)` macro should take an `:ident`, not an `:expr`.


macro_rules! show_lit {
    ($x:lit) => { $x }
}

macro_rules! choose {
    ($x:expr, $y:expr) => { if true { $x } else { $y } }
}

// `:lit` accepts numbers, strings, bools, nil.

print(show_lit!{42})
// expected: 42


print(show_lit!{"hi"})
// expected: hi


// `:expr` accepts arbitrary expressions.

print(choose!{10, 20})
// expected: 10


// You can build conditions inline.

print(choose!{1 + 2, 99})
// expected: 3

Desafío

Reescribe el macro assert! del capítulo anterior usando macro_rules! con dos brazos: ($cond:expr) con mensaje por defecto y ($cond:expr, $msg:lit) con mensaje personalizado. Verifica que ambas formas funcionan.

Buscar en Zolo

9 resultados

enespt-br