过程宏

过程宏(Procedural Macros)是 Rust 中用于在编译时生成或修改代码的强大工具,允许实现复杂的元编程逻辑。核心在于接收并输出表示代码的 TokenStream

1. 核心概念与工作原理

过程宏的工作原理是在编译过程中充当一个代码转换函数:
  • 输入记号流 (Input TokenStream): 编译器将宏调用处的源代码解析为一系列记号(Tokens)。
  • 宏函数执行: 这些记号流被传递给你定义的过程宏函数。
  • AST 操作: 在宏函数内部,通常使用 syn crate 将记号流解析为抽象语法树 (AST),这是一个结构化的代码表示。你可以使用普通的 Rust 代码逻辑来检查、修改或生成 AST 节点。
  • 生成记号流 (Output TokenStream): 使用 quote crate 将修改后的 AST 或新生成的代码转换回记号流。
  • 代码注入: 编译器用新的记号流替换原始的宏调用,并继续编译。 

2. 过程宏三种类型详解

类型
描述调用示例
自定义派生宏 为数据结构自动实现 Trait,是最常见的类型。 #[derive(MyMacro)] struct User;
属性宏 类似于装饰器,用于修饰函数、模块等,可以完全替换或包裹目标代码。 #[log_calls] fn my_func() {}
类函数宏 类似 macro_rules! 但更灵活,可以解析任意自定义语法。 sql!(SELECT * FROM users WHERE id = 1);

3. 详细实践指南:创建自定义派生宏

本指南将演示如何创建一个简单的派生宏,为结构体自动实现一个 say_hello() 方法。
前提工具
安装 cargo-expand,用于查看宏展开后的代码,方便调试。
cargo install cargo-expand
Step 1: 设置项目结构
过程宏必须定义在一个独立的 crate 中。
cargo new my_macro_crate --lib
cd my_macro_crate
修改 Cargo.toml 以声明这是一个过程宏 crate 并添加必要的依赖:
Cargo.toml
[package]
name = "test-derive"
version = "1.2.3-dev"
license = "GPL-2.0-only"
description = "Derive macros for test"
edition = "2021"
[lib]
proc-macro = true
[dependencies]
syn = { version = "2.0", features = ["full"] }
quote = "1.0"
proc-macro2 = "1.0" # 通常由 syn 和 quote 内部使用,但了解其存在有益
Step 2: 编写宏逻辑
在 src/lib.rs 中定义宏函数 using syn and quote:
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(HelloMacro)] // 1. 声明为派生宏,名称为 HelloMacro
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
    // 2. 将输入的 TokenStream 解析为 syn::DeriveInput AST结构
    let ast = parse_macro_input!(input as DeriveInput);

    // 3. 提取结构体或枚举的名称
    let name = &ast.ident;

    // 4. 使用 quote! 宏生成实现代码
    // #name 是一个占位符,将被 ast.ident 的值替换
    let expanded = quote! {
        impl #name {
            fn say_hello_macro() {
                println!("Hello, I am {}!", stringify!(#name));
            }
        }
    };

    // 5. 将生成的代码转换回 TokenStream 并返回
    TokenStream::from(expanded)
}
Step 3: 在另一个 Crate 中使用宏
创建一个新的 application crate 来测试你的宏:
cd ..
cargo new app
cd app
修改 app/Cargo.toml,添加对本地宏 crate 的依赖:
Cargo.toml
[dependencies]
test-drive = { path = "../my_macro_crate" }
在 app/src/main.rs 中使用 #[derive(HelloMacro)]:
use test-drive::HelloMacro;

#[derive(HelloMacro)]
struct Rocket;

#[derive(HelloMacro)]
struct Car;

fn main() {
    Rocket::say_hello_macro(); // 输出: Hello, I am Rocket!
    Car::say_hello_macro();    // 输出: Hello, I am Car!
}
Step 4: 运行和调试
运行 cargo run 来执行程序。
使用 cargo expand 在 app 目录中查看编译器实际编译的代码:
cargo expand
这将显示 #[derive(HelloMacro)] 被展开成实际的 impl 块,帮助你验证宏是否按预期工作。

参考资料:

1.过程宏

2.Rust声明式宏

3.rust语言内置属性

posted @ 2026-01-30 10:10  PKICA  阅读(4)  评论(0)    收藏  举报