本页目录

过程宏 (Procedural Macros)

过程宏在编译期运行 Rust 代码,接收 TokenStream、输出 TokenStream——derive 宏为 struct/enum 自动生成 impl,attribute 宏修饰任意项,function-like 宏像函数一样调用但处理 token 流。syn 解析 TokenStream 为 AST,quote 把 AST 转回 TokenStream,cargo expand 查看展开结果——三者是写过程宏的标准工作流。

三种过程宏

// 1. derive: #[derive(MyTrait)] — 为类型自动生成 impl
#[proc_macro_derive(MyTrait)]
pub fn my_derive(input: TokenStream) -> TokenStream { ... }

// 2. attribute: #[my_attr] — 修饰整个 item (fn, struct, impl)
#[proc_macro_attribute]
pub fn my_attr(attr: TokenStream, item: TokenStream) -> TokenStream { ... }

// 3. function-like: my_macro!(...) — 类似 macro_rules! 但用 Rust 函数处理输入
#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream { ... }

过程宏是 Rust 代码,运行在编译期接收和产出 TokenStream——一个 token 序列。这比 macro_rules! 更灵活——你可以做任意复杂的编译期计算。

syn + quote: 事实标准

use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};
use quote::quote;

#[proc_macro_derive(Hello)]
pub fn hello_derive(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);   // TokenStream → AST
    let name = input.ident;
    let gen = quote! {
        impl Hello for #name {
            fn hello() { println!("Hello, {}!", stringify!(#name)); }
        }
    };
    gen.into()                                               // 回到 TokenStream
}

synTokenStream 解析为 AST(类似 Rust 编译器的前端)。quote! 把 Rust 代码转回 token stream,支持插值(#name)。

proc-macro crate 的限制

  • 必须是单独的 crate(Cargo.toml: [lib] proc-macro = true
  • 只能导出 proc macro 函数,不能导出普通函数、类型或常量
  • 依赖不能是 cycle——proc macro crate 依赖其他 crate 但不能被同一 workspace 的 crate 依赖回

cargo expand: 展开宏

cargo install cargo-expand
cargo expand                          # 展开当前 crate 所有宏
cargo expand my_function              # 展开特定函数

这是理解宏行为的最快方式——看到编译器实际处理的代码。

参考

  • syn: docs.rs/syn, quote: docs.rs/quote
  • cargo-expand: github.com/dtolnay/cargo-expand

Keywords: proc macro, derive, syn, quote, TokenStream, cargo expand, attribute macro, function-like macro