Erro de Invocação de Macro no Rust

Como resolver erros de invocação de macro no Rust. Aprenda sobre a sintaxe correta, problemas comuns com println!, vec!, macro_rules! e macros procedurais.

Erro de Invocação de Macro

Erros de invocação de macro no Rust aparecem quando você chama uma macro com sintaxe incorreta, argumentos errados ou esquece o !. As mensagens de erro para macros podem ser menos intuitivas que as de funções normais, pois o compilador reporta o erro no código expandido.

As Mensagens de Erro

Esquecendo o !:

error[E0423]: expected function, found macro `println`
 --> src/main.rs:2:5
  |
2 |     println("Olá");
  |     ^^^^^^^ not a function
  |
help: use `!` to invoke the macro
  |
2 |     println!("Olá");
  |            +

Argumentos errados em println!:

error: 1 positional argument in format string, but no arguments were given
 --> src/main.rs:2:20
  |
2 |     println!("Olá, {}!");
  |                    ^^

Erro em vec!:

error: no rules expected the token `=>`
 --> src/main.rs:2:25
  |
2 |     let v = vec![1, 2 => 3];
  |                        ^^ no rules expected this token in macro call

Macro personalizada com padrão não reconhecido:

error: no rules expected the token `+`
  --> src/main.rs:10:20
   |
1  | macro_rules! calcular {
   | ---------------------- when calling this macro
...
10 |     calcular!(5 + 3);
   |                    ^ no rules expected this token in macro call

O Que Significa

Macros no Rust são expansões de código — elas geram código Rust baseado em padrões. Diferente de funções, macros:

  1. São invocadas com ! (ex: println!, vec!)
  2. Podem aceitar padrões variados de argumentos
  3. São expandidas antes da verificação de tipos
  4. Cada macro define suas próprias regras de aceitação de argumentos

Quando uma invocação não corresponde a nenhum padrão definido pela macro, o compilador reporta “no rules expected this token”. Quando faltam argumentos esperados, o erro aparece no formato da string ou no padrão esperado.

Código com Erro

Esquecendo o !

fn main() {
    // ERRO: sem `!`, o Rust procura uma função chamada `println`
    println("Olá, mundo!");

    // ERRO: mesmo problema
    let v = vec[1, 2, 3];
}

Argumentos Errados em Macros de Formatação

fn main() {
    // ERRO: placeholder {} sem argumento correspondente
    println!("Nome: {}, Idade: {}");

    // ERRO: argumento demais
    println!("Nome: {}", "Alice", 30);

    // ERRO: tipo incompatível com formatação
    println!("Valor: {:b}", "texto"); // :b é para inteiros
}

Erro em macro_rules! Personalizada

macro_rules! saudar {
    ($nome:expr) => {
        println!("Olá, {}!", $nome);
    };
}

fn main() {
    // ERRO: macro espera 1 expressão, recebe 2
    saudar!("Alice", "Bob");

    // ERRO: token inesperado
    saudar!(let x = 5);
}

Como Resolver

Solução 1: Adicionar o ! na Invocação

fn main() {
    println!("Olá, mundo!");     // Com !
    let v = vec![1, 2, 3];      // Com !
    format!("Texto: {}", 42);   // Com !
    assert_eq!(1 + 1, 2);       // Com !
}

Macros comuns da biblioteca padrão que precisam de !:

MacroUso
println! / eprintln!Imprimir no stdout/stderr
format!Formatar string
vec!Criar vetor
assert! / assert_eq!Asserções
panic!Causar panic
todo! / unimplemented!Marcar código incompleto
dbg!Debug rápido
include_str! / include_bytes!Incluir arquivo
cfg!Verificar configuração
matches!Pattern matching como expressão

Solução 2: Corrigir Formatação de String

fn main() {
    let nome = "Alice";
    let idade = 30;

    // Placeholders posicionais
    println!("Nome: {}, Idade: {}", nome, idade);

    // Placeholders nomeados
    println!("Nome: {nome}, Idade: {idade}");

    // Formatação específica
    println!("Pi: {:.2}", std::f64::consts::PI);   // 3.14
    println!("Hex: {:#x}", 255);                    // 0xff
    println!("Binário: {:08b}", 42);                // 00101010
    println!("Alinhado: {:>10}", "Rust");           //       Rust
    println!("Debug: {:?}", vec![1, 2, 3]);         // [1, 2, 3]
    println!("Pretty: {:#?}", vec![1, 2, 3]);       // Formatado
}

Solução 3: Corrigir macro_rules! Personalizada

Defina padrões claros para cada variação de uso:

macro_rules! saudar {
    // Padrão 1: um nome
    ($nome:expr) => {
        println!("Olá, {}!", $nome);
    };
    // Padrão 2: múltiplos nomes
    ($($nome:expr),+ $(,)?) => {
        $(
            println!("Olá, {}!", $nome);
        )+
    };
}

fn main() {
    saudar!("Alice");
    saudar!("Bob", "Carol", "Dave");
}

Tipos de fragmentos em macro_rules!:

macro_rules! exemplo {
    ($e:expr) => {};       // Expressão
    ($i:ident) => {};      // Identificador
    ($t:ty) => {};         // Tipo
    ($p:pat) => {};        // Padrão (pattern)
    ($b:block) => {};      // Bloco { ... }
    ($s:stmt) => {};       // Statement
    ($l:literal) => {};    // Literal
    ($tt:tt) => {};        // Token tree (qualquer token)
    ($($r:tt)*) => {};     // Repetição de tokens
}

Solução 4: Corrigir Macros Procedurais (derive)

// ERRO: derive com atributo errado
#[derive(Debug, Serialze)]  // Typo: "Serialze"
struct Dados {
    campo: String,
}

// CORRETO:
#[derive(Debug, serde::Serialize)]
struct Dados {
    campo: String,
}

Certifique-se de que o crate com a macro derive está no Cargo.toml:

[dependencies]
serde = { version = "1", features = ["derive"] }

Solução 5: Debug de Macros com cargo expand

Para ver o código que a macro gera:

# Instalar cargo-expand
cargo install cargo-expand

# Ver código expandido
cargo expand

# Ver expansão de um módulo específico
cargo expand modulo::submodulo

Dicas para Macros

  1. Sempre use ! para invocar macros
  2. Leia a documentação da macro para saber os padrões aceitos
  3. Use cargo expand para debugar macros complexas
  4. Prefira funções quando possível — macros são mais difíceis de debugar
  5. Use dbg! para debug rápido — imprime a expressão e seu valor
fn main() {
    let x = 5;
    let y = dbg!(x * 2) + 1;  // Imprime: [src/main.rs:3] x * 2 = 10
    println!("y = {}", y);     // y = 11
}

Veja Também