如何调试自定义Rust过程宏并查看其展开输出?
调试Rust自定义过程宏并查看展开输出的正确方法
一、解决「同一crate内无法使用自定义过程宏」的问题
Rust过程宏必须定义在独立的proc-macro crate中,无法在同一个crate内同时定义和直接使用(测试代码也受此限制)。正确的项目结构需要拆分三个模块:
- 公共trait crate:定义宏依赖的
trait,供宏和业务代码共同引用 - proc-macro crate:实现自定义过程宏
- 业务/测试crate:调用过程宏
标准项目结构
my_project/ ├── Cargo.toml ├── src/ │ └── main.rs ├── hello_macro/ # 公共trait crate │ ├── Cargo.toml │ └── src/ │ └── lib.rs └── hello_macro_derive/ # proc-macro crate ├── Cargo.toml └── src/ └── lib.rs
各模块代码配置
- hello_macro(公共trait crate)
Cargo.toml:
[package] name = "hello_macro" version = "0.1.0" edition = "2021"
src/lib.rs:
pub trait HelloMacro { fn hello_macro() -> String; }
- hello_macro_derive(proc-macro crate)
Cargo.toml必须声明为proc-macro类型:
[package] name = "hello_macro_derive" version = "0.1.0" edition = "2021" [lib] proc-macro = true [dependencies] syn = { version = "2.0", features = ["full"] } quote = "1.0" proc-macro2 = "1.0" hello_macro = { path = "../hello_macro" }
src/lib.rs(修正宏代码,引用公共trait):
use syn; use quote::quote; use proc_macro; use hello_macro::HelloMacro; #[proc_macro_derive(HelloMacro)] pub fn hello_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); impl_hello_macro(&ast) } fn impl_hello_macro(ast: &syn::DeriveInput) -> proc_macro::TokenStream { let name = &ast.ident; let gen = quote! { impl HelloMacro for #name { fn hello_macro() -> String { String::from(stringify!(#name)) } } }; gen.into() }
- 业务/测试crate
Cargo.toml:
[package] name = "my_project" version = "0.1.0" edition = "2021" [dependencies] hello_macro = { path = "./hello_macro" } hello_macro_derive = { path = "./hello_macro_derive" }
src/main.rs(含测试代码):
use hello_macro::HelloMacro; use hello_macro_derive::HelloMacro; #[derive(HelloMacro)] struct A; fn main() { println!("{}", A::hello_macro()); } #[cfg(test)] mod tests { use super::*; #[test] fn test_macro() { assert_eq!(A::hello_macro(), "A".to_string()); } }
二、使用cargo-expand查看宏展开(推荐)
解决版本兼容问题
cargo-expand必须与当前rustc版本匹配,执行以下命令同步版本:
rustup component add rustfmt --toolchain nightly cargo +nightly install cargo-expand
执行展开命令
- 查看主代码宏展开:
cargo +nightly expand - 查看测试代码宏展开:
cargo +nightly expand --test tests
三、用rustc直接展开的方法
若不想使用cargo-expand,可在业务crate目录下执行(注意替换平台对应的动态库后缀:Windows为.dll,macOS为.dylib):
rustc +nightly -Zunpretty=expanded src/main.rs --extern hello_macro=target/debug/deps/libhello_macro.rlib --extern hello_macro_derive=target/debug/deps/libhello_macro_derive.so
四、额外调试技巧
- 打印生成的代码:在宏实现中加入字符串转换打印,执行测试时用
cargo test -- --nocapture查看输出:let gen = quote! { ... }; println!("Generated code:\n{}", gen.to_string()); gen.into() - 查看语法树结构:对
syn的语法树节点调用.dump()方法,打印详细结构:println!("AST structure:\n{}", ast.dump());
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

