Pular para o conteúdo

macro_rules!

Nesta página

macro_rules! é a segunda forma de macro do Zolo, inspirada no Rust. A diferença fundamental em relação a macro name(p) { ... } é que você escreve braços de padrão — o expansor testa cada braço de cima para baixo e usa o primeiro que casa. Isso habilita sobrecarga de aridade, repetição variádica e restrições de captura.

Forma básica

O braço mais simples não captura nada: () => { expressão }. Invocação usa chaves: name!{}:

greet!{} sem parâmetros — 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

Repetição

O padrão $( $x:expr ),* captura zero ou mais expressões separadas por vírgula. No corpo, $( corpo )* repete uma vez por item capturado. Esse é o mecanismo por trás de macros variádicos como sum!{a, b, c}:

sum! acumula qualquer quantidade de expressões com $( ... ),*.

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últiplos braços e sobrecarga

Listando vários braços, você cobre diferentes aridades no mesmo macro. O expansor escolhe o primeiro braço cujo padrão se encaixa — comportamento idêntico ao match:

describe! com zero, um e dois argumentos — o primeiro braço que casa vence.

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 em macro_rules!

A mesma garantia de higiene da forma macro se aplica aqui: let interno recebe sufixo único e nunca sobrescreve bindings do chamador:

double_sum! declara acc internamente sem afetar o acc do chamador.

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

O sufixo após : restringe o tipo de token aceito no padrão: :expr aceita qualquer expressão, :ident apenas um identificador, :lit apenas um literal (número, string, bool, nil). Isso permite erros de misuse mais precisos em tempo de expansão:

show_lit! com :lit e choose! com :expr ilustram as restrições 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

Desafio

Reescreva o macro assert! do capítulo anterior usando macro_rules! com dois braços: ($cond:expr) com mensagem padrão e ($cond:expr, $msg:lit) com mensagem personalizada. Verifique que ambas as formas funcionam.

Buscar no Zolo

9 resultados

enespt-br