Macros Procedurais: Derive e Função
Aprofunde-se nas macros procedurais, incluindo macros `#[derive]` personalizadas e macros semelhantes a funções, para gerar código mais complexo.
Macros Procedurais: Derive e Função é uma aula grátis de Learn Rust Coding no CoddyKit. Esta é a aula 2 de 3. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Learn Rust Coding, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Learn Rust Coding inclui 3 aulas no total.
Introdução às macros procedurais
Bem-vindo ao mundo das macros procedurais! Diferentemente das macros declarativas (macro_rules!), as macros procedurais são semelhantes a funções que operam diretamente sobre o código Rust.
Elas recebem um TokenStream como entrada, processam-no e retornam outro TokenStream. Isso permite uma geração de código muito mais complexa.
Tipos de macros procedurais
O Rust oferece três tipos principais de macros procedurais:
- Macros semelhantes a funções: têm a aparência e o comportamento de chamadas de função comuns, por exemplo,
my_macro!(...). - Macros derive: permitem implementar características automaticamente para estruturas e enumerações usando
#[derive(MyTrait)]. - Macros de atributo: permitem definir atributos personalizados que podem ser aplicados a itens, por exemplo,
#[route("/path")].
A crate `proc-macro`
As macros procedurais devem ficar em seu próprio tipo especial de crate. No seu Cargo.toml, declare-a assim:
Isso informa ao Cargo que a crate contém macros que transformam código em tempo de compilação.
[package]
name = "my_macros"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = trueMacros semelhantes a funções explicadas
Macros semelhantes a funções são definidas com o atributo #[proc_macro] em uma função que recebe um proc_macro::TokenStream e retorna um.
Elas são úteis para DSLs personalizadas (linguagens específicas de domínio) ou para repetições complexas de código que macro_rules! não consegue tratar.
use proc_macro::TokenStream;
#[proc_macro]
pub fn my_function_macro(input: TokenStream) -> TokenStream {
// Logic to transform input TokenStream
// ...
input // For example, return the input unchanged
}Exemplo de macro semelhante a uma função
Aqui está um exemplo conceitual. Uma macro `greet_name!` que gera uma instrução de exibição. A implementação real analisaria a entrada TokenStream para extrair o nome.
Embora a definição da macro seja complexa, seu uso é simples e poderoso:
/* In 'my_macros/src/lib.rs' */
use proc_macro::TokenStream;
#[proc_macro]
pub fn greet_name(input: TokenStream) -> TokenStream {
// Imagine parsing 'input' to get a name like 'World'
// and generating: println!("Hello, World!");
"println!(\"Hello, {}!\", \"CoddyKit\");".parse().unwrap()
}
/* In 'my_app/src/main.rs' */
use my_macros::greet_name;
fn main() {
greet_name!("CoddyKit");
}Entendendo macros `#[derive]`
Macros derive são o tipo mais comum. Elas permitem implementar automaticamente uma característica para uma estrutura ou enumeração.
Quando você escreve #[derive(Debug)], uma macro é executada em tempo de compilação para gerar o código necessário para que seu tipo implemente a característica Debug.
Implementando um `#[derive]` (conceito)
As macros personalizadas de derivação normalmente são construídas usando bibliotecas como syn (para analisar código Rust em uma AST) e quote (para gerar código Rust a partir de uma AST).
Você definiria uma função anotada com #[proc_macro_derive(MyTrait)] que recebe um TokenStream representando a estrutura ou enumeração e retorna a implementação gerada do traço.
/* In 'my_macros/src/lib.rs' */
use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};
use quote::quote;
#[proc_macro_derive(MyDebug)] // MyDebug is the trait name
pub fn my_debug_derive(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let name = &ast.ident;
let expanded = quote! {
impl std::fmt::Debug for #name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(stringify!(#name)).finish()
}
}
};
expanded.into()
}Exemplo usando `#[derive(Debug)]`
Vamos ver um exemplo prático de uma macro de derivação que você provavelmente já usou: Debug. Ao adicionar #[derive(Debug)] à nossa estrutura Point, o Rust implementa automaticamente o traço Debug, permitindo imprimir seu conteúdo de forma organizada.
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 10, y: 20 };
println!("My point is: {:?}", p);
}Macros de atributos: `#[my_attribute]`
Macros de atributos são semelhantes às macros de derivação, mas podem ser aplicadas a qualquer item (funções, estruturas, módulos etc.). Elas recebem como entrada o item ao qual estão associadas, além de quaisquer argumentos entre parênteses.
Usos comuns incluem roteamento de estruturas web (por exemplo, #[get("/users")]) ou configuração de testes (por exemplo, #[test]).
/* In 'my_macros/src/lib.rs' */
use proc_macro::TokenStream;
#[proc_macro_attribute]
pub fn log_calls(attr: TokenStream, item: TokenStream) -> TokenStream {
// Imagine adding println! statements to 'item'
item
}
/* In 'my_app/src/main.rs' */
use my_macros::log_calls;
#[log_calls("entry_point")]
fn my_function() {
println!("Inside my_function");
}
fn main() {
my_function();
}Quando usar cada macro
- Macros semelhantes a funções: para minilinguagens personalizadas ou transformações complexas de blocos de código arbitrários.
- Macros de derivação: para implementar automaticamente traços em estruturas e enumerações.
- Macros de atributos: para modificar ou gerar código ao redor de itens específicos (funções, estruturas) com base em atributos personalizados.
Cada tipo tem uma finalidade própria na extensão da sintaxe e dos recursos do Rust.
Verificação rápida: tipos de macros
Quais das afirmações a seguir sobre macros procedurais são TRUE?
Recapitulação: o poder da geração de código
Você explorou o mundo avançado das macros procedurais!
- Aprendemos sobre macros semelhantes a funções para criar sintaxe personalizada.
- Entendemos como as macros
#[derive]automatizam implementações de traços. - Vimos brevemente as macros de atributos para geração de código no nível dos itens.
As macros procedurais são uma ferramenta poderosa para reduzir código repetitivo e ampliar os recursos do Rust, permitindo escrever código que escreve código!
Perguntas Frequentes
A aula “Macros Procedurais: Derive e Função” é grátis?
Sim — o texto completo de “Macros Procedurais: Derive e Função” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Learn Rust Coding, atualize para CoddyKit PRO. O curso de Learn Rust Coding inclui 3 aulas no total.
O que vou aprender em “Macros Procedurais: Derive e Função”?
Aprofunde-se nas macros procedurais, incluindo macros `#[derive]` personalizadas e macros semelhantes a funções, para gerar código mais complexo. Você pratica Learn Rust Coding com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Learn Rust Coding?
Nenhuma experiência prévia é necessária. Learn Rust Coding no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 3.
Quanto tempo leva a aula “Macros Procedurais: Derive e Função”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Learn Rust Coding?
Sim. Cada aula de Learn Rust Coding inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Macros Declarativas (`macro_rules!`)
- Macros Procedurais: Derive e Função
- Interação com Rust Não Seguro