Rust: Introdução às macros do Rust

Última atualização: 2026-08-26

As macros são as “máquinas geradoras de código” do Rust — basta escrever uma macro uma vez, e ela gera automaticamente inúmeras instâncias do mesmo código, livrando os programadores da tarefa repetitiva de copiar e colar.

Se uma função “encapsula a lógica para chamadas repetidas”, então uma macro “encapsula regras de geração de código para expansões repetidas”. As funções operam sobre valores em tempo de execução, enquanto as macros operam sobre o próprio código em tempo de compilação. É assim: uma função é uma linha de montagem de fábrica (que recebe matérias-primas e produz produtos), enquanto uma macro é uma planta da fábrica (que recebe desenhos de projeto e produz toda a linha de montagem).


1. O que você vai aprender


2. A história da máquina de imprimir dinheiro

(1) A agonia: o atoleiro do código duplicado

Tom é engenheiro de Rust em uma empresa de logística. Recebeu o que parecia ser uma tarefa simples: escrever uma função para cada um dos cinco meios de transporte (caminhão, navio, avião, trem e drone) para calcular os custos de frete e os prazos estimados de entrega.

Então, o que aconteceu?

“Seria ótimo se houvesse uma maneira de escrever uma regra uma única vez e fazer com que ela gerasse automaticamente todas as funções semelhantes...”

(2) A abordagem das macros em Rust

RUST
// Define using a macro"Shipping Cost Calculator"Template
macro_rules! create_shipping_calculator {
    // Matching Patterns: Name of Mode of Transportation + Rate per kilometer + Speed
    ($name:ident, $rate_per_km:expr, $speed:expr) => {
        fn $name(distance: f64) -> (f64, f64) {
            let base_cost = distance * $rate_per_km;
            let fuel_surcharge = base_cost * 0.1;
            let total = base_cost + fuel_surcharge;
            let time_hours = distance / $speed;
            (total, time_hours)
        }
    };
}

// Single-line macro call = Generate a complete function
create_shipping_calculator!(truck,  1.5,  60.0);
create_shipping_calculator!(ship,   0.8,  30.0);
create_shipping_calculator!(plane,  5.0, 800.0);
create_shipping_calculator!(train,  1.2,  80.0);
create_shipping_calculator!(drone,  2.0,  50.0);

fn main() {
    let distance = 500.0;
    // Every function exists by default.,Just like handwriting
    println!("Truck :  cost=${:.2}, time={:.1}h", truck(distance).0,  truck(distance).1);
    println!("Ship  :  cost=${:.2}, time={:.1}h", ship(distance).0,   ship(distance).1);
    println!("Plane :  cost=${:.2}, time={:.1}h", plane(distance).0,  plane(distance).1);
    println!("Train :  cost=${:.2}, time={:.1}h", train(distance).0,  train(distance).1);
    println!("Drone :  cost=${:.2}, time={:.1}h", drone(distance).0,  drone(distance).1);
}

Resultado:

TEXT 📖 Somente leitura
Truck :  cost=$825.00, time=8.3h
Ship  :  cost=$440.00, time=16.7h
Plane :  cost=$2750.00, time=0.6h
Train :  cost=$660.00, time=6.2h
Drone :  cost=$1100.00, time=10.0h

As macros são como “impressoras de código”: você cria um modelo (a chapa de impressão) e, toda vez que chama a macro, ela “imprime” um trecho completo de código. Quando você faz alterações, basta modificar um único ponto no modelo, e todo o código gerado é atualizado simultaneamente — não é mais necessário modificar manualmente cada função, uma a uma.


3. Conceitos fundamentais

(1) O processo de expansão macroeconômica

100%
graph TB
    A[macro_rules! Declaration Macro] --> B[Matching Arm 1: Pattern => Template]
    A --> C[Matching Arm 2: Pattern => Template]
    A --> D[Matching Arm N: Pattern => Template]

    B --> E[Compiler Matching input token]
    C --> E
    D --> E

    E --> F{Match Successful?}
    F -->|Yes| G[Replace the template and expand the code]
    F -->|No| H[Compilation Error: Mismatch]

    G --> I[Generate AST Node]
    I --> J[Continue compiling]

    style A fill:#4a90d9,color:#fff
    style E fill:#e6a23c,color:#fff
    style F fill:#f56c6c,color:#fff
    style G fill:#67c23a,color:#fff

(2) Comparação entre funções e macros

Dimensão Função (fn) Macro (macro_rules!)
Momento da execução Chamada em tempo de execução Expansão em tempo de compilação
Número de parâmetros Fixo Variável (por meio de padrões de repetição)
Tipo de parâmetro Tipo fixo Fluxo de tokens arbitrário
Geração de código Não gera código Gera uma nova AST de código
Valor de retorno Possui um tipo de retorno Pode gerar qualquer trecho de código
Uso foo(args) foo!(args) com um ponto de exclamação
Limites de recursão Profundidade da pilha Profundidade de recursão de macros (padrão: 128 níveis)
Higiene Âmbito do isolamento natural Declaração de higiene parcial macro

(3) Macros integradas comuns

Macro Função Exemplo
println! Imprimir na saída padrão e adicionar uma nova linha println!("Hello, {}!", name)
print! Imprimir na saída padrão sem caractere de nova linha print!("count: {}", i)
format! A string formatada retorna um String let s = format!("{}:{}", h, m)
vec! Criar vetor rapidamente let v = vec![1, 2, 3]
todo! Espaço reservado, causa um erro grave em tempo de execução fn foo() { todo!() }
unimplemented! Sinalizador não implementado, erro grave em tempo de execução fn bar() { unimplemented!() }
eprintln! Imprimir no stderr eprintln!("Error: {}", msg)
write! Tipos que implementam fmt::Write write!(&mut s, "{}", val)
concat! Concatenação de strings em tempo de compilação concat!("a", "b", "c")"abc"
stringify! Converte uma expressão em uma string literal stringify!(1+2)"1 + 2"

(4) Comparação entre macros de declaração e macros procedurais

Dimensão Macro de declaração macro_rules! Macro de procedimento (Proc Macro)
Método de definição macro_rules! name { ... } Caixa independente + #[proc_macro_*]
Momento da expansão Correspondência de padrões + substituição em tempo de compilação Geração de código procedural em tempo de compilação
Recursos Correspondência de padrões + substituição de tokens É capaz de ler/gerar qualquer AST
Complexidade Baixa (declarativa) Alta (requer a escrita de código em Rust para processar a AST)
Subcategoria Nenhuma Macros derivadas #[derive] / Macros de atributos #[attr] / Macros funcionais name!()
Aplicações típicas vec![], println! serde::Serialize, tokio::main
Dificuldade de depuração Moderada Alta

4. Exemplos de macros

▶ Exemplo 1: Declarando sua primeira macro — Gerando automaticamente funções getter (Dificuldade ⭐)

RUST
// ============================================
// Usage macro_rules! Define a Macro
// Scene: Automatically Generate getter Methods for Structure Fields
// ============================================

// Macro Definitions: Generate getter Function based on field name and type
// A macro name followed by ! Indicates that this is a macro
macro_rules! create_getter {
    // Matching Patterns: $name is an identifier (ident), $ty is a type (ty)
    // => The code block below is a template expansion.
    ($name:ident, $ty:ty) => {
        pub fn $name(&self) -> $ty {
            self.$name.clone()
        }
    };
}

// Structures That Use Macros
#[derive(Debug)]
struct Student {
    name: String,
    age: u32,
    grade: String,
}

impl Student {
    // Written by hand getter —— You have to write one for each field.
    // pub fn name(&self) -> String { self.name.clone() }
    // pub fn age(&self) -> u32 { self.age }
    // pub fn grade(&self) -> String { self.grade.clone() }

    // Automatically Generated Using a Macro —— One per line getter
    create_getter!(name, String);
    create_getter!(age, u32);
    create_getter!(grade, String);
}

fn main() {
    let s = Student {
        name: "Alice".to_string(),
        age: 20,
        grade: "A".to_string(),
    };

    // Generated by calling a macro getter Methods
    println!("Name  : {}", s.name());
    println!("Age   : {}", s.age());
    println!("Grade : {}", s.grade());
}

Resultado:

TEXT 📖 Somente leitura
Name  : Alice
Age   : 20
Grade : A

A macro create_getter! recebe um nome de campo e um tipo, e se expande em uma definição completa de pub fn. Quando expandida, três chamadas à create_getter! equivalem a escrever três funções getter manualmente. Esse é o protótipo da “impressora de código” — o modelo define as regras de geração de código, e cada chamada imprime um trecho de código.


▶ Exemplo 2: Padrões de repetição $()* e $()+ — Macros com parâmetros variáveis (Dificuldade ⭐⭐)

RUST
// ============================================
// Repetition Patterns in Presentation Macros:$()* Zero or more times、$()+ Once or multiple times
// Scene: A Mini Test Framework, Supports multiple assertions
// ============================================

// Macro: Run Multiple Test Cases, each case includes a name + Expression + Expected Value
// $()* Indicates that the pattern inside the parentheses can be repeated zero or more times
macro_rules! run_tests {
    // Each test consists of (Name, Expression, Expected Value) Composition of Trios
    // $test_name It is an identifier,$expr It is an expression,$expected It is an expression
    ($( $test_name:ident, $expr:expr, $expected:expr );* $(;)?) => {
        $(
            println!("[Test] {} ...", stringify!($test_name));
            let result = $expr;
            let expected: i32 = $expected;
            if result == expected {
                println!("  ✅ PASS: {} == {}", result, expected);
            } else {
                println!("  ❌ FAIL: {} != {} (expected {})", stringify!($expr), result, expected);
            }
        )*
    };
}

// Macro: Calculate the sum of any number of values
// $()+ Indicates that the pattern inside the parentheses must be repeated at least once
macro_rules! sum_of {
    // Usage $()+ At least one argument is required.
    ($($x:expr),+ $(,)?) => {
        // 0 + $x Cumulative total: 0 + a + b + c ...
        {
            let mut sum = 0i64;
            $(
                sum += $x as i64;
            )+
            sum
        }
    };
}

fn main() {
    println!("=== Mini Test Framework ===");

    // Call run_tests! macro - Pass multiple test cases
    run_tests! {
        add_one,    1 + 1, 2;
        multiply,   3 * 4, 12;
        subtract,   10 - 3, 7;
        power,      2 * 2 * 2, 8
    }

    println!("\n=== Sum Calculator ===");

    // Call sum_of! macro - Pass any number of arguments
    let s1 = sum_of!(1, 2, 3, 4, 5);
    println!("sum_of!(1..5) = {}", s1);

    let s2 = sum_of!(10, 20, 30);
    println!("sum_of!(10,20,30) = {}", s2);

    // A single parameter is also acceptable.
    let s3 = sum_of!(42);
    println!("sum_of!(42) = {}", s3);

    println!("\n=== Done ===");
}

Resultado:

TEXT 📖 Somente leitura
=== Mini Test Framework ===
[Test] add_one ...
  ✅ PASS: 2 == 2
[Test] multiply ...
  ✅ PASS: 12 == 12
[Test] subtract ...
  ✅ PASS: 7 == 7
[Test] power ...
  ✅ PASS: 8 == 8

=== Sum Calculator ===
sum_of!(1..5) = 15
sum_of!(10,20,30) = 60
sum_of!(42) = 42

=== Done ===

$()* e $()+ são fundamentais para implementar “argumentos variáveis” em macros. $()* indica “zero ou mais repetições” (por exemplo, Vec pode ficar vazio), enquanto $()+ indica “uma ou mais repetições” (pelo menos um argumento). Dentro dos padrões de repetição, delimitadores (,, ;, etc.) também podem ser usados para controlar como os parâmetros são agrupados.


▶ Exemplo 3: Implementação simplificada da macro vec! — Entendendo como funcionam as macros embutidas (Dificuldade ⭐⭐)

RUST
// ============================================
// Implement a simplified version of the vec! macro
// Understanding vec! The Underlying Principles of Macro Expansion
// ============================================

// Simplified vec! macro - Does not support vec![x; n] syntax
macro_rules! my_vec {
    // Empty vector
    () => {
        Vec::new()
    };
    // A single element
    ($elem:expr) => {
        {
            let mut v = Vec::new();
            v.push($elem);
            v
        }
    };
    // Multiple elements,Separated by commas
    ($($x:expr),+ $(,)?) => {
        {
            let mut v = Vec::new();
            $(
                v.push($x);
            )+
            v
        }
    };
}

fn main() {
    // Use the built-in vec! macro
    let builtin_empty: Vec<i32> = vec![];
    let builtin_one = vec![42];
    let builtin_multi = vec![1, 2, 3, 4, 5];

    println!("=== Built-in vec! ===");
    println!("empty  : {:?}", builtin_empty);
    println!("one    : {:?}", builtin_one);
    println!("multi  : {:?}", builtin_multi);

    // Use Custom my_vec! macro
    let my_empty: Vec<i32> = my_vec![];
    let my_one = my_vec![42];
    let my_multi = my_vec![10, 20, 30, 40, 50];

    println!("\n=== Custom my_vec! ===");
    println!("my_empty : {:?}", my_empty);
    println!("my_one   : {:?}", my_one);
    println!("my_multi : {:?}", my_multi);

    // Verify Functional Consistency
    assert_eq!(builtin_multi.len(), 5);
    assert_eq!(my_multi.len(), 5);
    assert_eq!(builtin_multi, vec![1, 2, 3, 4, 5]);
    assert_eq!(my_multi, vec![10, 20, 30, 40, 50]);

    println!("\n=== All assertions passed! ===");
}

Resultado:

TEXT 📖 Somente leitura
=== Built-in vec! ===
empty  : []
one    : [42]
multi  : [1, 2, 3, 4, 5]

=== Custom my_vec! ===
my_empty : []
my_one   : [42]
my_multi : [10, 20, 30, 40, 50]

=== All assertions passed! ===

A macro vec! que você vê é, na verdade, uma macro de declaração macro_rules!! Ela usa $($x:expr),+ para corresponder a uma lista de expressões separadas por vírgulas e, em seguida, se expande em blocos repetidos de código v.push($x). Isso é “algo que uma função não pode fazer” — vec![1, 2, 3] Se fosse escrita como uma função, seria impossível determinar o número de elementos no momento da compilação e gerar o código push correspondente.


▶ Exemplo 4: Como usar as macros integradas todo! e unimplemented! (Dificuldade: ⭐)

RUST
// ============================================
// Demonstrate Built-in Macros: todo! / unimplemented! / format! / eprintln!
// Scene: An inventory management system currently under development
// ============================================

// Simulated Inventory Items
#[derive(Debug)]
struct InventoryItem {
    id: u32,
    name: String,
    quantity: u32,
}

// Inventory Manager
struct InventoryManager {
    items: Vec<InventoryItem>,
}

impl InventoryManager {
    fn new() -> InventoryManager {
        InventoryManager {
            items: Vec::new(),
        }
    }

    // Implemented: Add Item
    fn add_item(&mut self, id: u32, name: &str, quantity: u32) {
        self.items.push(InventoryItem {
            id,
            name: name.to_string(),
            quantity,
        });
        // Usage format! Macro-Formatted Log Messages
        let log_msg = format!("[INFO] Added item: {} (id={}, qty={})", name, id, quantity);
        println!("{}", log_msg);
    }

    // Implemented: Search for Products
    fn find_item(&self, id: u32) -> Option<&InventoryItem> {
        self.items.iter().find(|item| item.id == id)
    }

    // Not implemented: Update Inventory
    fn update_quantity(&mut self, _id: u32, _new_qty: u32) {
        // TODO: Implement the inventory update logic
        // todo!() will panic with a "Not implemented" message
        todo!("update_quantity: id={} quantity={}", _id, _new_qty);
    }

    // Not implemented: Generate an Inventory Report
    fn generate_report(&self) -> String {
        // unimplemented!() Indicates that this feature has not been implemented yet
        unimplemented!("generate_report() is not yet implemented");
    }

    // Implemented: Print All Items
    fn list_items(&self) {
        if self.items.is_empty() {
            println!("  (no items in inventory)");
            return;
        }
        for item in &self.items {
            println!("  #{} {} (qty: {})", item.id, item.name, item.quantity);
        }
    }
}

fn main() {
    let mut manager = InventoryManager::new();

    println!("=== Inventory Manager ===");

    // Add Item
    manager.add_item(101, "Laptop", 10);
    manager.add_item(102, "Mouse", 50);
    manager.add_item(103, "Keyboard", 30);

    // List Products
    println!("\nCurrent inventory:");
    manager.list_items();

    // Search for Products
    if let Some(item) = manager.find_item(102) {
        println!("\nFound: {:?}", item);
    }

    // Uncommenting the following code will panic (But it won't result in a compilation error):
    // manager.update_quantity(101, 8);   // panics: "not yet implemented"
    // let report = manager.generate_report(); // panics: "not yet implemented"

    println!("\n=== Demo completed ===");
    println!("Note: Try uncommenting update_quantity() or generate_report() to see todo!/unimplemented! in action.");
}

Resultado:

TEXT 📖 Somente leitura
=== Inventory Manager ===
[INFO] Added item: Laptop (id=101, qty=10)
[INFO] Added item: Mouse (id=102, qty=50)
[INFO] Added item: Keyboard (id=103, qty=30)

Current inventory:
  #101 Laptop (qty: 10)
  #102 Mouse (qty: 50)
  #103 Keyboard (qty: 30)

Found: InventoryItem { id: 102, name: "Mouse", quantity: 50 }

=== Demo completed ===
Note: Try uncommenting update_quantity() or generate_report() to see todo!/unimplemented! in action.

todo!() e unimplemented!() são “poderosas ferramentas de espaço reservado” para desenvolvedores de Rust. todo!() pode incluir uma descrição (todo!("msg: {}", val)), tornando-o ideal para marcar funcionalidades ainda não concluídas durante o desenvolvimento. unimplemented!() é mais adequado para marcar métodos em definições de interface que ainda não foram implementados. Ambos causarão um panic em tempo de execução, mas não resultarão em erros de compilação — permitindo que você escreva o código primeiro e o implemente passo a passo.


▶ Exemplo 5: Higiene de macros — Variáveis internas de macros não contaminam o ambiente externo (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// Demonstrating Macro Hygiene
// Variables created within a macro do not conflict with those in the outer scope.
// ============================================

// Note: Rust declaration macros have "partial hygiene"
// For internal use by Hong $ Captured variable names will not conflict with external ones
// However, identifiers written directly within the macro are in Rust 2018+ There are special rules in this case

// Macro: Create a local variable tmp and swap values
// Note: tmp written directly within the macro is's hygienic.——It will not affect the outside world. tmp
macro_rules! swap_with_tmp {
    ($a:expr, $b:expr) => {
        {
            let tmp = $a;
            $a = $b;
            $b = tmp;
        }
    };
}

// Macro: Demonstrate Hygiene - Even if there is a tmp variable outside
macro_rules! demonstrate_hygiene {
    ($x:expr) => {
        {
            // Defined internally by the macro tmp It's hygienic.
            let tmp = $x * 2;
            println!("  Inside macro: tmp = {}", tmp);
            tmp
        }
    };
}

// Counterexample of Unhygienic Conditions (Demonstrated differently)
// Note: Rust declarative macros do not allow creating variables that cause cross-scope conflicts.
// So here we use concat Simulation"Non-health-related"Potential Issues

fn main() {
    println!("=== Macro Hygiene Demonstration ===\n");

    // Scenario 1: Internal variables in a macro do not affect external variables
    println!("1. Macro internal variable vs external variable:");
    let mut x = 10;
    let mut y = 20;

    println!("  Before swap: x={}, y={}", x, y);

    // The macro uses the following internally: tmp,But the external variable names tmp Not affected
    swap_with_tmp!(x, y);

    println!("  After swap:  x={}, y={}", x, y);

    // External tmp The variable does not exist. —— Inside the macro tmp It's hygienic.
    // If you uncomment the following line, you'll get a compilation error.:
    // println!("tmp from macro = {}", tmp);  // ❌ Compilation Error: tmp not found

    // Scenario 2: Internal macro variables do not conflict with external variables of the same name
    println!("\n2. Hygiene with same name:");
    let tmp = 100;  // External tmp
    println!("  Outside macro: tmp = {}", tmp);

    let result = demonstrate_hygiene!(5);
    println!("  Return value: {}", result);
    println!("  Outside macro again: tmp = {}", tmp);  // It's still 100,Not affected by macros

    // Scenario 3: Why Is Hygiene Important?
    println!("\n3. Why hygiene matters:");
    println!("  Without hygiene, macros could accidentally:");
    println!("  - Overwrite variables in the caller's scope");
    println!("  - Create hard-to-find bugs");
    println!("  - Break encapsulation of the calling code");
    println!("  Rust's hygiene prevents these issues at compile time.");

    println!("\n=== Demo completed ===");
}

Resultado:

TEXT 📖 Somente leitura
=== Macro Hygiene Demonstration ===

1. Macro internal variable vs external variable:
  Before swap: x=10, y=20
  After swap:  x=20, y=10

2. Hygiene with same name:
  Outside macro: tmp = 100
  Inside macro: tmp = 10
  Return value: 10
  Outside macro again: tmp = 100

3. Why hygiene matters:
  Without hygiene, macros could accidentally:
  - Overwrite variables in the caller's scope
  - Create hard-to-find bugs
  - Break encapsulation of the calling code
  Rust's hygiene prevents these issues at compile time.

A higiene de macros é uma característica importante das macros do Rust: os nomes de variáveis criados dentro de uma macro não “vazam” para o escopo do chamador. No exemplo acima, há um tmp dentro da macro e um tmp fora dela, mas eles não interferem um no outro. Isso evita os problemas de “conflito de nomes” comuns nas macros do C — no C, se uma macro usa uma variável chamada tmp e o chamador por acaso tiver uma variável chamada tmp, isso pode levar a bugs difíceis de depurar.


▶ Exemplo 6: Exemplo abrangente — Criação de uma mini-estrutura de testes com macros (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// Comprehensive Example: Building a Mini Unit Testing Framework Using Macros
// Use in combination:Matching Patterns、Repetition Pattern、Built-in Macros
// ============================================

// Test Results Summary
struct TestStats {
    total: u32,
    passed: u32,
    failed: u32,
}

impl TestStats {
    fn new() -> TestStats {
        TestStats { total: 0, passed: 0, failed: 0 }
    }

    fn print_summary(&self) {
        println!("\n==============================");
        println!("Test Summary:");
        println!("  Total : {}", self.total);
        println!("  Passed: {}", self.passed);
        println!("  Failed: {}", self.failed);
        if self.failed == 0 {
            println!("  ✅ All tests passed!");
        } else {
            println!("  ❌ {} test(s) failed", self.failed);
        }
        println!("==============================");
    }
}

// Macro: Define a set of test cases
// Each test is identified by its name、Assertion Expressions、Expected Results Breakdown
macro_rules! test_suite {
    // Matches zero or more tests
    ($( $name:ident: $left:expr, $op:tt, $right:expr );* $(;)?) => {{
        let mut stats = TestStats::new();
        $(
            stats.total += 1;
            print!("[Test] {}: {} {} {} ... ", stringify!($name),
                stringify!($left), stringify!($op), stringify!($right));

            let passed = match $op {
                == => { $left == $right }
                != => { $left != $right }
                <  => { $left < $right }
                <= => { $left <= $right }
                >  => { $left > $right }
                >= => { $left >= $right }
                _ => { panic!("Unsupported operator: {}", stringify!($op)); }
            };

            if passed {
                stats.passed += 1;
                println!("✅ PASS");
            } else {
                stats.failed += 1;
                println!("❌ FAIL (got {:?}, expected {:?})", $left, $right);
            }
        )*
        stats
    }};
}

fn main() {
    println!("=== Mini Test Framework ===");
    println!("Using macro-generated test suite\n");

    // Test Suites Defined Using Macros
    let stats = test_suite! {
        test_add:  2 + 2, ==, 4;
        test_sub:  10 - 3, ==, 7;
        test_mul:  3 * 4, ==, 12;
        test_div:  10 / 2, ==, 5;
        test_gt:   100, >, 50;
        test_lt:   3, <, 10;
        test_eq:   "hello", ==, "hello";
        test_neq:  42, !=, 0
    };

    // Print Statistics
    stats.print_summary();

    // Verify that all tests have passed
    assert_eq!(stats.total, 8);
    assert_eq!(stats.passed, 8);
    assert_eq!(stats.failed, 0);

    println!("\n=== Demo completed ===");
}

Resultado:

TEXT 📖 Somente leitura
=== Mini Test Framework ===
Using macro-generated test suite

[Test] test_add: 2 + 2 == 4 ... ✅ PASS
[Test] test_sub: 10 - 3 == 7 ... ✅ PASS
[Test] test_mul: 3 * 4 == 12 ... ✅ PASS
[Test] test_div: 10 / 2 == 5 ... ✅ PASS
[Test] test_gt: 100 > 50 ... ✅ PASS
[Test] test_lt: 3 < 10 ... ✅ PASS
[Test] test_eq: "hello" == "hello" ... ✅ PASS
[Test] test_neq: 42 != 0 ... ✅ PASS

==============================
Test Summary:
  Total : 8
  Passed: 8
  Failed: 0
  ✅ All tests passed!
==============================

=== Demo completed ===

Este exemplo abrangente demonstra o verdadeiro poder das macros: a macro test_suite! recebe um conjunto de definições de teste (nome, expressão, operador de comparação, valor esperado) e as expande automaticamente em código completo de execução de teste. Oito casos de teste escritos usando a macro requerem apenas oito linhas de código para serem chamados, enquanto escrever o código equivalente manualmente exigiria pelo menos 60 linhas. Mais importante ainda, se você precisar adicionar recursos como “tempos limite de teste” ou “agrupamento de testes”, basta modificar um único ponto no modelo da macro, e todos os testes serão atualizados automaticamente.


❓ Perguntas Frequentes

P: Qual é exatamente a diferença entre macros e funções? Quando se deve usar uma macro em vez de uma função? R: As macros expandem seu código em tempo de compilação, enquanto as funções são executadas em tempo de execução. Macros podem fazer coisas que funções não conseguem: lidar com um número variável de argumentos (como vec![1, 2, 3]), gerar código na fase de compilação e aceitar trechos de código como argumentos. No entanto, macros são mais difíceis de depurar, resultam em código menos legível e podem levar a mensagens de erro obscuras na fase de compilação. Priorize o uso de funções e use macros apenas quando precisar fazer algo que as funções não conseguem.

P: Qual é a diferença entre $()* e $()+ em macro_rules!? R: $()* corresponde a zero ou mais repetições (incluindo uma string vazia), enquanto $()+ corresponde a uma ou mais repetições (pelo menos uma). Por exemplo, vec![] (vetor vazio) usa $()*, enquanto vec![1, 2] pode usar tanto $()+ quanto $()*. Se você quiser que a macro aceite pelo menos um argumento, use $()+; se permitir zero argumento, use $()*.

P: O que é higiene de macros? Por que ela é importante? R: A higiene se refere ao fato de que as variáveis criadas dentro de uma macro não entram em conflito com as variáveis do escopo externo. Em C, as macros são substituições de texto, portanto, conflitos de nomes podem surgir facilmente — por exemplo, se uma macro usa tmp internamente, mas o chamador também tiver uma variável chamada tmp. As macros de declaração do Rust são “parcialmente higiênicas”: os nomes de variáveis capturados por $ dentro da macro não vazam, evitando assim esses bugs.

P: Qual é a diferença entre todo!() e unimplemented!()? R: Ambos causam um erro de pânico em tempo de execução, mas sua semântica difere. todo!() indica que “esse recurso está planejado, mas ainda não foi implementado” e pode incluir parâmetros para especificar o progresso (por exemplo, todo!("implement pagination")). unimplemented!() indica que “esta interface/método não está planejado para implementação no momento”. todo!() é mais comumente usado durante o desenvolvimento para marcar itens na lista de tarefas, enquanto unimplemented!() é mais comumente usado em implementações padrão de traits.

P: O que é uma macro procedural? R: Uma macro procedural é um sistema de macros mais poderoso do que as macros declarativas; ela não realiza “correspondência e substituição de padrões”, mas sim “recebe código → executa uma função em Rust → gera um novo código”. Existem três tipos: macros derivadas #[derive(...)] (como #[derive(Debug)]), macros de atributos (como #[test]) e macros de função (como #[async], que está por trás de async fn). As macros procedimentais devem ser definidas em um crate proc-macro separado, enquanto as macros de declaração macro_rules! podem ser definidas em qualquer lugar. Esta lição aborda apenas os conceitos; o uso detalhado das macros procedimentais é um assunto avançado.


📖 Resumo


📝 Exercícios

  1. Dificuldade ⭐: Escreva uma macro make_pair! que receba duas expressões como argumentos e retorne uma tupla (expr1, expr2). Por exemplo, make_pair!(42, "hello") se expande para (42, "hello"). Chame-a em main e imprima o resultado.
  2. Dificuldade ⭐⭐: Escreva uma macro assert_equal! que receba duas expressões como argumentos. Se elas forem iguais, exiba ✅ PASS; caso contrário, exiba ❌ FAIL: left != right. Use a macro stringify! para exibir as expressões originais. Crie pelo menos 3 casos de teste (incluindo casos em que as expressões sejam iguais e casos em que não sejam) e execute-os.
  3. Dificuldade ⭐⭐⭐: Escreva uma macro create_enum_with_display! que receba como entrada um nome de enumeração e um conjunto de nomes de variantes, e gere automaticamente a definição da enumeração e a implementação da característica Display (com cada variante exibida como sua string correspondente). Por exemplo, create_enum_with_display!(Color, Red, Green, Blue) se expande em uma enumeração Color, na qual Red é exibido como "Red". Dica: use o padrão de repetição $()* e stringify!.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%