跳转至

宏编程

宏编程

📚 章节概述

宏是 Rust 中强大的元编程工具,允许你在编译时生成代码。本章将介绍声明宏、过程宏和宏规则,帮助你理解如何使用宏来减少代码重复和提高开发效率。

🎯 学习目标

  • 理解宏的概念和作用
  • 掌握声明宏的使用
  • 学会编写自定义宏
  • 理解过程宏的类型
  • 掌握宏的最佳实践
  • 了解宏的调试技巧

📖 宏基础

1.1 宏与函数的区别

Rust
// 函数:运行时调用
fn add(a: i32, b: i32) -> i32 {
    a + b
}

// 宏:编译时展开
macro_rules! add {
    ($a:expr, $b:expr) => {
        $a + $b
    };
}

fn main() {
    let result = add!(1, 2);
    println!("{}", result);
}

1.2 宏的优势

  • 编译时展开:零运行时开销
  • 可变参数:支持任意数量的参数
  • 类型灵活:可以接受不同类型的参数
  • 代码生成:减少重复代码

📝 声明宏

2.1 基本语法

Rust
macro_rules! say_hello {
    () => {
        println!("Hello!");
    };
}

fn main() {
    say_hello!();
}

2.2 宏参数

Rust
macro_rules! create_function {
    ($func_name:ident) => {
        fn $func_name() {
            println!("Function {} called", stringify!($func_name));
        }
    };
}

create_function!(foo);
create_function!(bar);

fn main() {
    foo();
    bar();
}

2.3 多种模式

Rust
macro_rules! calculate {
    // 单个参数
    (add $a:expr, $b:expr) => {
        $a + $b
    };

    // 多个参数
    (sum $($e:expr),*) => {
        {
            let mut sum = 0;
            $(sum += $e;)*
            sum
        }
    };
}

fn main() {
    let result1 = calculate!(add 1, 2);
    let result2 = calculate!(sum 1, 2, 3, 4);
    println!("{} {}", result1, result2);
}

2.4 重复模式

Rust
macro_rules! vec_of_strings {
    ($($x:expr),*) => {
        {
            let mut temp_vec = Vec::new();
            $(temp_vec.push($x.to_string());)*
            temp_vec
        }
    };
}

fn main() {
    let v = vec_of_strings!("hello", "world", "rust");
    println!("{:?}", v);
}

2.5 标识符和表达式

Rust
macro_rules! print_result {
    (ident $name:ident) => {
        println!("Identifier: {}", stringify!($name));
    };

    (expr $expr:expr) => {
        println!("Expression: {}", $expr);
    };
}

fn main() {
    let x = 5;
    print_result!(ident x);
    print_result!(expr 1 + 2);
}

🔧 常用宏

3.1 vec! 宏

Rust
// 标准库中的 vec! 宏
let v1 = vec![1, 2, 3];
let v2 = vec![0; 10]; // 10 个 0

3.2 println! 和 format!

Rust
let name = "Alice";
println!("Hello, {}!", name);

let s = format!("Hello, {}!", name);

3.3 panic! 和 assert!

Rust
panic!("Something went wrong!");

assert_eq!(1, 1);
assert_ne!(1, 2);

3.4 自定义宏示例

Rust
macro_rules! hashmap {
    ($($key:expr => $val:expr),*) => {
        {
            let mut map = std::collections::HashMap::new();
            $(map.insert($key, $val);)*
            map
        }
    };
}

fn main() {
    let scores = hashmap!("Alice" => 100, "Bob" => 90);
    println!("{:?}", scores);
}

🔬 过程宏

4.1 过程宏类型

  • 派生宏:为结构体和枚举自动实现 trait
  • 属性宏:为项添加自定义属性
  • 函数式宏:类似声明宏但更强大

4.2 派生宏

Rust
use serde::Serialize;

#[derive(Serialize)]
struct User {
    name: String,
    age: u32,
}

4.3 自定义派生宏

Rust
// Cargo.toml
[lib]
proc-macro = true

[dependencies]
syn = "2"
quote = "1.0"
proc-macro2 = "1.0"
Rust
// src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

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

    let gen = quote! {
        impl HelloMacro for #name {
            fn hello_macro() {
                println!("Hello, Macro! My name is {}", stringify!(#name));
            }
        }
    };

    gen.into()
}

📝 练习题

练习 1:基础宏

Rust
// TODO: 创建一个宏,计算两个数的乘积
macro_rules! multiply {
    // TODO: 实现
}

fn main() {
    let result = multiply!(3, 4);
    println!("Result: {}", result);
}

练习 2:重复模式

Rust
// TODO: 创建一个宏,生成一个包含多个元素的 Vec
macro_rules! create_vec {
    // TODO: 实现
}

fn main() {
    let v = create_vec!(1, 2, 3, 4, 5);
    println!("{:?}", v);
}

练习 3:条件宏

Rust
// TODO: 创建一个宏,根据条件打印不同的消息
macro_rules! conditional_print {
    // TODO: 实现
}

fn main() {
    conditional_print!(true, "This is true");
    conditional_print!(false, "This is false");
}

练习 4:结构体宏

Rust
// TODO: 创建一个宏,快速定义简单的结构体
macro_rules! simple_struct {
    // TODO: 实现
}

fn main() {
    // TODO: 使用宏创建结构体
}

💡 最佳实践

1. 宏命名规范

Rust
// 好:使用 snake_case,名称清晰描述用途
macro_rules! create_map {
    ($($key:expr => $val:expr),*) => { /* ... */ };
}

// 避免:含糊或过于简短的名称
macro_rules! cm {
    ($($k:expr => $v:expr),*) => { /* ... */ };
}

2. 文档化宏

Rust
/// 创建一个简单的计数器
///
/// # 示例
/// ```
/// let counter = create_counter!();
/// ```
macro_rules! create_counter {
    () => {
        0
    };
}

3. 限制宏作用域

Rust
// 好:使用 #[macro_export] 使宏公开
#[macro_export]
macro_rules! public_macro {
    () => { /* ... */ }
}

// 避免:宏默认私有
macro_rules! private_macro {
    () => { /* ... */ }
}

4. 使用宏减少重复

Rust
// 好:使用宏减少重复
macro_rules! impl_ops {
    ($type:ty, $field:ident) => {
        impl std::ops::Add for $type {
            type Output = $type;
            fn add(self, other: $type) -> $type {
                Self { $field: self.$field + other.$field }
            }
        }
    };
}

// 使用示例:
// struct MyType { value: i32 }
// impl_ops!(MyType, value);

// ℹ️ 下面是错误示范:直接使用 self + other 会无限递归调用 Add::add
// 避免:手动实现中使用 self + other
impl std::ops::Add for MyType {
    type Output = MyType;
    fn add(self, other: MyType) -> MyType {
        self + other // ⚠️ 无限递归!编译通过但运行时栈溢出
    }
}

⚠️ 常见错误

1. 宏展开错误

Rust
// 错误:宏展开后代码不正确
macro_rules! bad_macro {
    ($x:expr) => {
        let y = $x;
        y + 1
    };
}

fn main() {
    let result = bad_macro!(5); // 编译错误
}

// 正确:使用块表达式
macro_rules! good_macro {
    ($x:expr) => {
        {
            let y = $x;
            y + 1
        }
    };
}

2. 变量捕获

Rust
// 注意:Rust 的 macro_rules! 具有部分卫生性(hygiene)
// 宏内部定义的变量与外部同名变量不会冲突
macro_rules! example_macro {
    ($x:expr) => {
        {
            let y = 1;  // 这个 y 与调用处的 y 处于不同的语法上下文
            $x + y
        }
    };
}

fn main() {
    let y = 2;
    let result = example_macro!(y); // result = 2 + 1 = 3,不会产生冲突
    println!("{}", result);
}

// 最佳实践:使用下划线前缀避免混淆
macro_rules! good_macro {
    ($x:expr) => {
        {
            let _y = 1;
            $x + _y
        }
    };
}

3. 递归深度

Rust
// 错误:无限递归,将导致编译错误
macro_rules! recursive_macro {
    () => { recursive_macro!() };
}

// ⚠️ 注意:以下写法实际上无法正常工作!
// 宏的模式匹配基于 token,而非值的运算结果
// `$n - 1` 作为表达式传入后不会匹配字面量 `0`
// 正确的递归宏应使用 token 计数或其他技术
macro_rules! recursive_macro {
    (0) => { 0 };
    ($n:expr) => {
        $n + recursive_macro!($n - 1)
    };
}

🔍 调试宏

1. 使用 cargo expand

Bash
cargo install cargo-expand
cargo expand

2. 打印宏展开

Rust
macro_rules! debug_macro {
    ($x:expr) => {
        {
            eprintln!("Macro input: {:?}", stringify!($x));
            let result = $x;
            eprintln!("Macro output: {:?}", result);
            result
        }
    };
}

fn main() {
    let result = debug_macro!(1 + 2);
}

📚 扩展阅读

🎯 本章小结

本章介绍了 Rust 的宏编程:

  • ✅ 理解宏的概念和作用
  • ✅ 掌握声明宏的使用
  • ✅ 学会编写自定义宏
  • ✅ 理解过程宏的类型
  • ✅ 掌握宏的最佳实践
  • ✅ 了解宏的调试技巧

下一章: 我们将学习 Rust 的异步编程,包括 async/await、Future 和运行时。