Macros em Rust entre arquivos: tutorial completo para desenvolvimento modular

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Macros em Rust: como fazer amizade com os módulos?



Você já escreveu uma macro útil em Rust e depois tentou usá-la em um módulo vizinho — e recebeu um erro misterioso do compilador? Se sim, você não está sozinho. A pergunta "How do I use a macro across module files?" acumulou milhares de visualizações no Stack Overflow, e não é à toa.



Em Rust, macros são uma ferramenta poderosa de metaprogramação. Elas permitem gerar código em tempo real, evitar duplicação e escrever DSL (linguagens específicas de domínio). Mas o problema é: as macros não se comportam como funções comuns. Elas têm suas próprias regras de visibilidade, e os iniciantes frequentemente tropeçam nisso.



Neste tutorial, mostrarei três maneiras comprovadas de organizar macros entre arquivos modulares. Vamos lá!



Por que as macros não funcionam "por padrão"?



Antes de mergulhar na solução, vamos entender a causa. Em Rust, funções e tipos são visíveis apenas em seu próprio módulo por padrão (a menos que marcados com pub). Com macros, a história é a mesma, mas há uma nuance: pub para macros não funciona da mesma forma que para funções.



Considere um exemplo simples. Suponha que temos um projeto com esta estrutura:



src/├── main.rs├── helpers/│   ├── mod.rs│   └── macros.rs


Em macros.rs, declaramos uma macro:



// src/helpers/macros.rsmacro_rules! greet {    ($name:expr) => {        println!("Olá, {}!", $name);    };}


Agora queremos usá-la em main.rs:



// src/main.rsmod helpers;

fn main() { greet!("Mundo"); // Erro! Macro não está visível.}


O compilador emitirá algo como: error: macro `greet` is not imported. Por quê? Porque as macros não são exportadas do módulo por padrão, mesmo que o módulo seja declarado como pub.



Método 1: Atributo #[macro_use] para importar macros



A maneira mais simples é usar o atributo #[macro_use] antes da declaração do módulo. Isso faz com que o Rust importe todas as macros desse módulo para o escopo atual.



Vamos corrigir nosso exemplo:



// src/main.rs#[macro_use]mod helpers;

fn main() { greet!("Mundo"); // Agora funciona!}


Mas há um porém: #[macro_use] importa todas as macros do módulo. Se você tiver muitas macros, isso pode causar conflitos de nomes. Além disso, essa abordagem é considerada obsoleta no Rust moderno (a partir da edição 2018).



Onde isso ainda é útil? Em projetos pequenos ou quando você está escrevendo um protótipo e quer conectar rapidamente algumas macros sem complicações.



Nuance importante: ordem de declaração



Se você usar #[macro_use], as macros ficam disponíveis imediatamente após a declaração do módulo. Isso significa que o código a seguir não funcionará:



// src/main.rsfn use_greet() {    greet!("Teste"); // Erro! O módulo ainda não foi declarado.}

#[macro_use]mod helpers;

fn main() { use_greet();}


A ordem importa. Tenha isso em mente.



Método 2: Exportação explícita de macros com #[macro_export]



Uma maneira mais moderna e controlada é usar o atributo #[macro_export] diretamente na definição da macro. Isso torna a macro acessível no nível do crate, não apenas do módulo.



Veja como funciona:



// src/helpers/macros.rs#[macro_export]macro_rules! greet {    ($name:expr) => {        println!("Olá, {}!", $name);    };}


Agora, em main.rs, podemos importar a macro explicitamente com use:



// src/main.rsmod helpers;

use helpers::greet;

fn main() { greet!("Mundo"); // Funciona!}


Observe: usamos use helpers::greet, e não use hel

Blogs

Book Recommendations