---
title: "Macros em Rust: Declarativas e Procedurais — Guia Completo 2026"
url: "https://rustlang.com.br/blog/macros-rust-declarativas-procedurais-2026/"
markdown_url: "https://rustlang.com.br/blog/macros-rust-declarativas-procedurais-2026.MD"
description: "Aprenda macros em Rust: macro_rules!, derive macros, attribute macros e function-like. Exemplos práticos com syn, quote e casos reais de uso."
date: "2026-03-29"
author: "Equipe Rust Brasil"
---

# Macros em Rust: Declarativas e Procedurais — Guia Completo 2026

Aprenda macros em Rust: macro_rules!, derive macros, attribute macros e function-like. Exemplos práticos com syn, quote e casos reais de uso.


## Introdução

Macros são uma das ferramentas mais poderosas de Rust — elas permitem **gerar código em tempo de compilação**, eliminando boilerplate e criando abstrações impossíveis com funções normais. Se você já usou `println!()`, `vec![]`, `#[derive(Debug)]` ou `#[tokio::main]`, você já usou macros.

Em Rust, existem dois tipos principais: **macros declarativas** (`macro_rules!`) e **macros procedurais** (derive, attribute e function-like). Cada tipo tem seus casos de uso, vantagens e complexidades. Neste guia, vamos explorar ambos com exemplos práticos que você pode aplicar nos seus projetos.

Se você está começando com Rust, recomendamos primeiro nosso [tutorial de primeiros passos](/tutoriais/primeiros-passos/) e depois voltar a este artigo.

## Macros Declarativas com macro_rules!

As macros declarativas usam **pattern matching** no código-fonte. Elas são definidas com `macro_rules!` e funcionam como um "match" sobre tokens de Rust. Para entender melhor pattern matching em Rust, confira nosso artigo sobre [pattern matching avançado](/artigos/pattern-matching-avancado/).

### Exemplo Básico: vec! Simplificado

```rust
// Reimplementação simplificada do vec![]
macro_rules! meu_vec {
    // Caso: lista de elementos — meu_vec![1, 2, 3]
    ( $( $elemento:expr ),* ) => {
        {
            let mut v = Vec::new();
            $( v.push($elemento); )*
            v
        }
    };
    // Caso: repetição — meu_vec![0; 5] cria vec com 5 zeros
    ( $elemento:expr ; $contagem:expr ) => {
        vec![$elemento; $contagem]
    };
}

fn main() {
    let numeros = meu_vec![1, 2, 3, 4, 5];
    let zeros = meu_vec![0; 10];
    println!("Números: {:?}", numeros); // [1, 2, 3, 4, 5]
    println!("Zeros: {:?}", zeros);     // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
}
```

### Fragmentos de Macro (Designators)

Os designators definem que tipo de token a macro aceita:

| Designator | Aceita | Exemplo |
|-----------|--------|---------|
| `$x:expr` | Expressão | `1 + 2`, `foo()` |
| `$x:ident` | Identificador | `minha_var`, `MeuTipo` |
| `$x:ty` | Tipo | `i32`, `Vec<String>` |
| `$x:pat` | Padrão | `Some(x)`, `_` |
| `$x:stmt` | Declaração | `let x = 5` |
| `$x:block` | Bloco | `{ println!("oi"); }` |
| `$x:tt` | Token tree | Qualquer token |
| `$x:literal` | Literal | `42`, `"texto"` |

### Exemplo Prático: Macro de HashMap

Uma macro que simplifica a criação de HashMaps — útil em qualquer projeto. Para mais sobre HashMaps, veja nosso guia da [stdlib HashMap](/stdlib/hashmap/).

```rust
macro_rules! hashmap {
    ( $( $chave:expr => $valor:expr ),* $(,)? ) => {
        {
            let mut mapa = std::collections::HashMap::new();
            $( mapa.insert($chave, $valor); )*
            mapa
        }
    };
}

fn main() {
    let config = hashmap! {
        "host" => "localhost",
        "porta" => "8080",
        "debug" => "true",
    };

    for (chave, valor) in &config {
        println!("{}: {}", chave, valor);
    }
}
```

### Macro Recursiva: DSL de SQL

Macros podem ser recursivas, permitindo criar mini-DSLs:

```rust
macro_rules! query {
    // SELECT campos FROM tabela
    (SELECT $( $campo:ident ),+ FROM $tabela:ident) => {
        format!(
            "SELECT {} FROM {}",
            vec![ $( stringify!($campo) ),+ ].join(", "),
            stringify!($tabela)
        )
    };
    // SELECT campos FROM tabela WHERE condição
    (SELECT $( $campo:ident ),+ FROM $tabela:ident WHERE $condicao:expr) => {
        format!(
            "SELECT {} FROM {} WHERE {}",
            vec![ $( stringify!($campo) ),+ ].join(", "),
            stringify!($tabela),
            $condicao
        )
    };
}

fn main() {
    let sql = query!(SELECT nome, email FROM usuarios);
    println!("{}", sql);
    // Output: SELECT nome, email FROM usuarios

    let sql2 = query!(SELECT id, nome FROM produtos WHERE "preco > 100");
    println!("{}", sql2);
    // Output: SELECT id, nome FROM produtos WHERE preco > 100
}
```

## Macros Procedurais

Macros procedurais são programas Rust que recebem código como entrada e geram código como saída. São mais poderosas que `macro_rules!` mas também mais complexas. Existem três tipos:

### 1. Derive Macros

As derive macros geram implementações automaticamente. Você já usa isso com `#[derive(Debug, Clone, Serialize)]`. Vamos criar uma derive macro que gera um método `descricao()`:

```rust
// Em uma crate separada: minha_macro/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(Descricao)]
pub fn derive_descricao(input: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(input as DeriveInput);
    let nome = &ast.ident;

    let expandido = quote! {
        impl #nome {
            pub fn descricao() -> &'static str {
                concat!("Struct: ", stringify!(#nome))
            }
        }
    };

    TokenStream::from(expandido)
}
```

```rust
// No código que usa a macro
use minha_macro::Descricao;

#[derive(Descricao, Debug)]
struct Usuario {
    nome: String,
    email: String,
}

fn main() {
    println!("{}", Usuario::descricao());
    // Output: Struct: Usuario
}
```

### 2. Attribute Macros

Attribute macros transformam o item anotado. O exemplo mais famoso é `#[tokio::main]`:

```rust
// Exemplo conceitual de como #[tokio::main] funciona
#[proc_macro_attribute]
pub fn meu_async_main(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let func = parse_macro_input!(item as syn::ItemFn);
    let corpo = &func.block;

    let expandido = quote! {
        fn main() {
            tokio::runtime::Runtime::new()
                .unwrap()
                .block_on(async #corpo)
        }
    };

    TokenStream::from(expandido)
}
```

Para aprender mais sobre o runtime Tokio e `async/await`, confira nosso [guia do ecossistema Tokio](/ecossistema/tokio/) e o artigo sobre [async Rust](/blog/async-rust-ecossistema-2026/).

### 3. Function-like Macros

Parecem chamadas de função mas são macros procedurais:

```rust
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
    let query = input.to_string();
    // Validar SQL em tempo de compilação...
    let expandido = quote! {
        sqlx::query!(#query)
    };
    TokenStream::from(expandido)
}

// Uso:
// let resultado = sql!(SELECT * FROM usuarios WHERE id = $1);
```

## Crates Essenciais: syn e quote

As crates **syn** e **quote** são fundamentais para macros procedurais:

- **syn**: Faz o parsing de código Rust em uma AST (Abstract Syntax Tree)
- **quote**: Gera código Rust a partir de templates com `quote!{}`

```toml
# Cargo.toml da crate de macros
[lib]
proc-macro = true

[dependencies]
syn = { version = "2", features = ["full"] }
quote = "1"
proc-macro2 = "1"
```

### Exemplo Completo: Derive Builder

Um padrão muito útil é gerar builders automaticamente. Para entender o padrão Builder em Rust, veja nosso artigo sobre [padrão Builder](/padroes/builder/):

```rust
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Data, Fields};

#[proc_macro_derive(Builder)]
pub fn derive_builder(input: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(input as DeriveInput);
    let nome = &ast.ident;
    let builder_nome = syn::Ident::new(
        &format!("{}Builder", nome),
        nome.span(),
    );

    // Extrair campos da struct
    let campos = match &ast.data {
        Data::Struct(data) => match &data.fields {
            Fields::Named(fields) => &fields.named,
            _ => panic!("Builder só funciona com named fields"),
        },
        _ => panic!("Builder só funciona com structs"),
    };

    // Gerar campos do builder como Option<T>
    let builder_campos = campos.iter().map(|f| {
        let nome = &f.ident;
        let tipo = &f.ty;
        quote! { #nome: Option<#tipo> }
    });

    // Gerar setters
    let setters = campos.iter().map(|f| {
        let nome = &f.ident;
        let tipo = &f.ty;
        quote! {
            pub fn #nome(mut self, val: #tipo) -> Self {
                self.#nome = Some(val);
                self
            }
        }
    });

    let expandido = quote! {
        pub struct #builder_nome {
            #( #builder_campos, )*
        }

        impl #nome {
            pub fn builder() -> #builder_nome {
                #builder_nome {
                    #( #(campos.iter().map(|f| &f.ident)): None, )*
                }
            }
        }

        impl #builder_nome {
            #( #setters )*
        }
    };

    TokenStream::from(expandido)
}
```

## Quando Usar Macros vs Generics vs Traits

Uma dúvida comum é quando usar macros em vez de generics ou traits. Para um entendimento profundo de traits e generics, veja nosso [tutorial de traits e generics](/tutoriais/traits-generics/).

| Use Macros Quando | Use Generics/Traits Quando |
|-------------------|---------------------------|
| Precisa gerar código variável | O comportamento segue um padrão tipado |
| Quer criar uma DSL | A abstração é sobre tipos |
| Precisa de variadic arguments | Os parâmetros são fixos |
| Quer verificações em compile-time | Runtime dispatch é aceitável |
| Quer eliminar boilerplate repetitivo | A composição de traits resolve |

## Casos Reais no Ecossistema

Macros são usadas extensivamente no ecossistema Rust. Alguns exemplos notáveis:

- **serde**: `#[derive(Serialize, Deserialize)]` — veja nosso [guia completo do Serde](/artigos/serde-guia-completo/)
- **thiserror**: `#[derive(Error)]` — como vimos no [artigo sobre tratamento de erros](/blog/tratamento-erros-rust-thiserror-anyhow/)
- **clap**: `#[derive(Parser)]` — para CLIs, veja nosso [tutorial de CLI com Clap](/tutoriais/cli-com-clap/)
- **tokio**: `#[tokio::main]` e `#[tokio::test]`
- **sqlx**: `query!()` com verificação SQL em compile-time

A crate [Rayon](/blog/rayon-paralelismo-dados-rust/) também usa macros internamente para implementar traits de iteração paralela automaticamente.

## Debugging de Macros

Depurar macros pode ser desafiador. Algumas ferramentas essenciais — que você pode integrar no seu [fluxo de CI/CD](/artigos/ci-cd-rust/):

```bash
# Expandir macros para ver o código gerado
cargo expand

# Expandir apenas um módulo específico
cargo expand nome_do_modulo

# Ver a expansão de uma macro específica
cargo expand --test nome_do_teste
```

O `cargo expand` requer instalação: `cargo install cargo-expand`. Para mais ferramentas do ecossistema Cargo, confira nosso artigo sobre [Cargo e ferramentas essenciais](/artigos/cargo-ferramentas-essenciais/).

```rust
// Outra técnica: compile_error! para debugging
macro_rules! debug_macro {
    ($($tokens:tt)*) => {
        compile_error!(concat!(
            "Tokens recebidos: ",
            stringify!($($tokens)*)
        ));
    };
}
```

## Boas Práticas com Macros

1. **Documente extensivamente** — macros são mais difíceis de entender que funções. Use a convenção de [documentação Rust](/artigos/documentacao-rust/).

2. **Prefira funções e traits** quando possível — macros devem ser o último recurso.

3. **Use `$crate::`** para referenciar itens da sua crate dentro de macros exportadas.

4. **Teste com `trybuild`** para macros procedurais — verifica erros de compilação esperados.

5. **Mantenha macros pequenas** — extraia lógica complexa para funções helper.

```rust
// Boa prática: macro delega para função
macro_rules! log_evento {
    ($nivel:expr, $($arg:tt)*) => {
        _log_evento_impl($nivel, &format!($($arg)*))
    };
}

fn _log_evento_impl(nivel: &str, mensagem: &str) {
    // Lógica complexa aqui — testável e debugável
    println!("[{}] {}", nivel, mensagem);
}
```

## Conclusão

Macros em Rust são uma ferramenta essencial que, quando usada com critério, pode transformar a qualidade do seu código. Use `macro_rules!` para padrões repetitivos e DSLs simples, e macros procedurais com syn/quote para geração de código sofisticada.

A regra de ouro é: **se uma função ou trait resolve, use-os**. Macros devem ser reservadas para casos onde a metaprogramação realmente agrega valor — eliminando boilerplate significativo ou criando APIs impossíveis de outra forma.

Para continuar aprendendo sobre técnicas avançadas, explore também <a href="https://golang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go Brasil</a> para ver como Go lida com geração de código via `go generate`, e <a href="https://ziglang.com.br/artigos/comptime-zig-metaprogramacao/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'ziglang.com.br' })">Zig Brasil</a> para ver como `comptime` oferece uma abordagem diferente para metaprogramação.

## Veja Também

- [Closures em Rust](/artigos/closures-rust/) — frequentemente usadas junto com macros
- [Iteradores em Rust](/artigos/iteradores-rust/) — macros que geram implementações de Iterator
- [Smart Pointers](/artigos/smart-pointers/) — derive macros como `Deref` e `DerefMut`
- [Top Projetos Open Source em Rust](/blog/top-projetos-open-source-rust-2026/) — muitos usam macros extensivamente
