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:
- São invocadas com
!(ex:println!,vec!) - Podem aceitar padrões variados de argumentos
- São expandidas antes da verificação de tipos
- 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 !:
| Macro | Uso |
|---|---|
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
- Sempre use
!para invocar macros - Leia a documentação da macro para saber os padrões aceitos
- Use
cargo expandpara debugar macros complexas - Prefira funções quando possível — macros são mais difíceis de debugar
- 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
}