「过程宏」part1

「这是我参与11月更文挑战的第 10 天,活动详情查看:2021最后一次更文挑战


构建过程宏,要在cargo.toml里面设置一些参数,这是必须的。一般来说,过程宏必须是一个库,或者作为工程的子库,不能单独作为一个源文件存在,至少目前不行。

所以在 cargo new [proc-lib] --lib 中一般需要标明:

[lib]
proc-macro = true
path = "src/lib.rs"
复制代码

从分类上,可以分为大致3类:

  • proc-macro
  • proc-macro-derive
  • proc-macro-attribute

大致说一下这几种的形态:

格式

简略过一下这几个宏的格式,在后面会具体讲他们的使用范围:

proc-macro

#[proc_macro]
pub fn my_proc_macro(input: TokenStream) -> TokenStream{
    // ...
}
复制代码

可以看出函数式的过程宏只接受一个形参,而且必须是pub的。

proc_macro_derive

#[proc_macro_derive(MyDerive)]
pub fn my_proc_macro_derive(input: TokenStream) -> TokenStream{
    // ...
}
复制代码

proc_macro_derive 表明了这是继承宏,还定义了新的继承宏的名字 MyDerive。 熟悉rust编程的,都应该知道有个继承宏,一直用得到,就是 Debug。这是标准库里的,可以帮助调试和显示。

proc_macro_attribute

#[proc_macro_attribute]
pub fn my_attribute_macro(attr: TokenStream, item: TokenStream) -> TokenStream {
    // ...
}
复制代码

可以看到这里的形参是两个,使用的关键字是 proc_macro_attribute。 关于例子,熟悉python的人应该知道修饰器吧,其实本质就是函数(闭包)可以作为一个对象来返回。 比如我需要一个修饰器来测量一个调用函数的运行时间。

示例

使用代码:

#[handler(blog(::hxl::com))]
fn fake(a: i32) {
    println!("hello proc-derive -> fake func args: {}", a);
}
复制代码

宏代码:

扫描二维码关注公众号,回复: 13204147 查看本文章
#[proc_macro_attribute]
pub fn handler(attr: TokenStream, item: TokenStream) -> TokenStream {
    eprintln!("attr: {:#?}", attr);
    eprintln!("item: {:#?}", item);
    item
}
复制代码

cargo check 我们可以在终端中看到:

可以看到目前 attr/item 其实都只是被识别成一个个标志符,没有任何意义,因为要把这些标志符组合有很大的难度。所以需要引入:syn/quote

syn & quote


#[proc_macro_attribute]
pub fn handler(attr: TokenStream, item: TokenStream) -> TokenStream {
    let args: AttributeArgs = parse_macro_input!(attr as AttributeArgs);
    eprintln!("args: {:#?}", args);

    let body = parse_macro_input!(item as Item);
    eprintln!("body: {:#?}", body);
    quote!(#body).into()
}
复制代码
  1. parse_macro_input! -> 是将 TokenStream 转换为语法树
    • 当识别成 AttributeArgs,输出的是 Vec<NestedMeta>
    • 当识别成 Item,输出的是 Item
  2. quote! -> 可以将语法树及其节点转换为 TokenStream
    • #body_ast 并不属于 Rust 表达式,是 quote! 自己实现的一种自定义语法
    • 返回值是 proc_macro2::TokenStream,通过 into() 才能转换,这个有历史原因
    • quote/syn/proc_macro2 目的都是为了更方便处理和编辑以及生成 proc_macro::TokenStream

猜你喜欢

转载自juejin.im/post/7031536095288360967