macro_rules!
macro_rules! define macros declarativas por meio de correspondência de padrões de árvores de tokens e expansão para código Rust. Elas são a primeira ferramenta para DSLs leves e código repetitivo (boilerplate).
Receita
macro_rules! hashmap {
($($k:expr => $v:expr),* $(,)?) => {{
let mut m = std::collections::HashMap::new();
$( m.insert($k, $v); )*
m
}};
}Quando usar isso: Sintaxe repetitiva que funções não conseguem abstrair (padrões em nível de token).
Exemplo de Trabalho
macro_rules! assert_returned {
($expr:expr, $pat:pat) => {
match $expr {
$pat => {}
other => panic!("inesperado: {:?}", other),
}
};
}
#[test]
fn parses() {
assert_returned!(parse("42"), Ok(42));
}O que isso demonstra:
- Múltiplos braços para diferentes padrões
$(,)?vírgula final opcional- Expansão higiênica no mesmo crate
- Exportação com
#[macro_export]para visibilidade na raiz do crate
Mergulho Profundo
Braços de Correspondência
macro_rules! op {
(add $a:expr, $b:expr) => { $a + $b };
(mul $a:expr, $b:expr) => { $a * $b };
}Macros Recursivas
macro_rules! count {
() => { 0 };
($head:tt $(, $tail:tt)*) => { 1 + count!($($tail),*) };
}- Recursão limitada pelo limite de recursão do compilador
- Prefira repetição não recursiva quando possível
Armadilhas
- Mensagens de erro opacas - erros apontam para o local da expansão. Correção:
compile_error!com texto claro para entrada inválida. - Múltiplos braços ambíguos - correspondência surpreendente. Correção: ordene os braços do mais específico para o menos específico; teste com
cargo expand. - Importar macro de um crate - precisa de
#[macro_export]oupub use. Correção: documente o caminho da macromy_crate::hashmap!. - Surpresas na inferência de tipo - o código expandido difere do código escrito à mão. Correção: adicione tipos explícitos na expansão.
- Uso excessivo para ajudantes simples - pior experiência do desenvolvedor do que uma função. Correção: use funções como padrão.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
const fn / genéricos | Lógica em tempo de compilação expressável como uma fn | Precisa de sintaxe personalizada |
| Proc macro | Transformações complexas de AST | Modelos de token simples |
build.rs codegen | Módulos grandes gerados | Açúcar sintático de call-site pequeno |
FAQs
macro_rules vs proc macro?
macro_rules para modelos de padrão; proc para AST completo (derive, atributos).
Como exportar uma macro?
#[macro_export] coloca na raiz do crate; re-exporte com pub use crate::macro_name;.
Macros podem ser async?
Expande para um bloco async dentro de uma fn; a macro em si não é async.
especificador tt?
Árvore de tokens - captura subárvores de tokens arbitrárias para correspondências flexíveis.
Múltiplas expressões?
Use $( $e:expr );* com separação por ponto e vírgula.
Documentar macros?
/// em um bloco macro_rules! documenta em rustdoc.
Macros da Edição 2024?
Mesmo modelo macro_rules; siga a edição para caminhos use de código gerado.
Testar macros?
Teste unitário do código expandido através de fns públicas que a macro envolve; ou teste de integração dos tipos de saída da macro.
uso de compile_error?
($($rest:tt)*) => { compile_error!("hashmap! requer key => value"); }; como braço de fallback.
Limitar recursão?
#![recursion_limit = "256"] na raiz do crate se uma macro recursiva profunda for necessária.
Relacionados
- Noções Básicas de Macros - introdução
- Depurando Macros - cargo expand
- Proc Macros do Tipo Função - DSLs mais pesadas
- Melhores Práticas de Macros
Versões da Pilha: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4 e Polars 0.46+.