Introdução: por que Rust sabe "fazer mágica" com código?
\n\nVocê já escreveu código em Rust e sentiu que estava repetindo as mesmas construções para dezenas de estruturas? Ou gostaria de gerar automaticamente a implementação de uma trait, sem copiar código manualmente? Bem-vindo ao mundo da metaprogramação em Rust — técnicas que permitem escrever código que escreve código.
\n\nRust não possui reflexão completa em tempo de execução (como Java ou Python), mas oferece algo mais poderoso e seguro — macros. Elas funcionam em tempo de compilação, analisando e transformando a árvore sintática abstrata (AST). Isso oferece possibilidades incríveis para otimização e redução de código boilerplate sem perda de desempenho.
\n\nNeste artigo, abordaremos técnicas avançadas de metaprogramação: desde os clássicos macro_rules! até macros procedurais completas. Você aprenderá como criar seus próprios atributos derive e escrever macros de atributo que alteram a semântica do código. Vamos lá!
Macros declarativas: macro_rules! — não é apenas "substituição de texto"
\n\nMuitos pensam que macro_rules! é um pré-processador de texto primitivo. Na verdade, é um poderoso sistema de correspondência de padrões, operando no nível de tokens. Ele permite criar DSLs (linguagens específicas de domínio) diretamente dentro do Rust.
Exemplo avançado: criando um builder com macro
\n\nImagine que você precisa criar um "construtor" (builder) para uma estrutura. Em vez de escrever dezenas de métodos, você pode escrever uma única macro:
\n\nmacro_rules! builder {\n ($struct_name:ident, { $($field:ident: $field_type:ty),* $(,)? }) => {\n struct $struct_name {\n $($field: Option<$field_type>),*\n }\n\n impl $struct_name {\n $(\n pub fn $field(mut self, value: $field_type) -> Self {\n self.$field = Some(value);\n self\n }\n )*\n\n pub fn build(self) -> Result<Self, String> {\n $(\n if self.$field.is_none() {\n return Err(format!("Campo {} é obrigatório", stringify!($field)));\n }\n )*\n Ok(self)\n }\n }\n };\n}\n\nbuilder!(Config, {\n host: String,\n port: u16,\n timeout: u64,\n});\n\nfn main() {\n let config = Config {\n host: None,\n port: None,\n timeout: None,\n }\n .host("localhost".to_string())\n .port(8080)\n .build()\n .unwrap();\n println!("Config pronto: {}:{}", config.host.unwrap(), config.port.unwrap());\n}\n\n\nEsta macro não apenas gera os campos, mas também adiciona validação de campos obrigatórios. Observe o stringify!($field) — ele converte o identificador em uma string em tempo de compilação. Esta é uma técnica típica de metaprogramação avançada em Rust.
Repetições e iterações: $(...)* em ação
\n\nA sintaxe de repetição permite processar listas de argumentos de qualquer comprimento. Combinando ident, ty, expr e outros "fragmentos", você pode criar padrões complexos. Por exemplo, uma macro para criar um enum com implementação automática de Display:
macro_rules! display_enum {\n ($enum_name:ident, { $($variant:ident => $desc:expr),* $(,)? }) => {\n enum $enum_name {\n $($variant),*\n }\n\n impl std::fmt::Display for $enum_name {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n match self {\n $(Self::$variant => write!(f, "{}", $desc)),*\n }\n }\n }\n };\n}\n\ndisplay_enum!(Status, {\n Active => "status_active",\n Inactive => "status_inactive",\n Pending => "s