前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Rust那些事之过程宏

Rust那些事之过程宏

作者头像
公众号guangcity
发布2023-02-28 15:47:41
4600
发布2023-02-28 15:47:41
举报
文章被收录于专栏:光城(guangcity)

Rust那些事之过程宏

1.过程宏

过程宏是一种扩展Rust编译器和提供可用于扩展该语言的插件的方法。过程宏的工作原理非常简单:取一段称为输入TokenStream的代码,将其转换为抽象语法树(ast),从输入处获得的内容构建一个新的TokenStream(使用syn::parse()方法),并将其作为输出代码注入编译器。

最近在项目中频繁看到各种使用,例如:zombodb中如下使用:

代码语言:javascript
复制
#[pg_extern(immutable, parallel_safe)]

在我们平时debug时:

代码语言:javascript
复制
#[derive(Debug)]

那么我们如何实现类似Debug的功能呢?

2.derive宏

使用侧:

代码语言:javascript
复制
#[derive(StructShow)]
pub struct Point {
    x: f64,
    y: f64
}

我们需要使用proc-macro来实现该功能。

代码语言:javascript
复制
cargo new --lib my-macro

在Cargo.toml中设置:

代码语言:javascript
复制
[lib]
proc-macro = true

[dependencies]
syn = { version = "1.0.82", features = ["full", "extra-traits"] }
quote = "1.0.10"

一个最基本的例子为:

代码语言:javascript
复制
use proc_macro::TokenStream;
use syn::parse;

#[proc_macro_derive(StructShow)]
pub fn whatever_you_want(tokens: TokenStream) -> TokenStream {
    let ast: syn::DeriveInput = syn::parse(tokens).unwrap();
    TokenStream::new()
}

在这个例子中使用了proc_macro,但是对于proc_macro有如下缺点:

  • 需要在proc-macro类型的crate中才能使用
  • 不太好进行测试

因此proc-macro2解决了这些问题,api也基本兼容。

代码语言:javascript
复制
#[proc_macro_derive(StructShow)]
pub fn show(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let output = transform_stream(proc_macro2::TokenStream::from(input));
    proc_macro::TokenStream::from(output)
}

在这里将核心操作转入transform_stream函数,在该函数中使用的是proc_macro2,因此可以方便测试在其他crate中直接使用。

随后,内部实现debug功能。

功能非常简单,要实现debug功能,只需要实现Debug trait即可,因此使用quote!将rust语法转位TokenStream返回给编译器即可。

代码语言:javascript
复制
fn transform_stream(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
    let ast: ItemStruct = syn::parse2(input).unwrap();
    let struct_type = ast.ident;
    let implemented_show = quote! {
        // 下面就是Display trait的定义了
        // use std::fmt; // 不要这样import,因为std::fmt是全局的,无法做到卫生性(hygiene)
        // 编译器会报错重复import fmt当你多次使用Show之后
        impl std::fmt::Display for #struct_type {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                write!(f, "{} x: {}, y: {}", stringify!(#struct_type), self.x, self.y)
            }
        }
    };
    implemented_show.into()
}

这样,我们在进行打印输出的时候便可以debug了。

代码语言:javascript
复制
let p= Point{x: 1.1, y: 2.3};
println!("{}", p); 

今天写的这个例子比较简单,过程宏功能很强大,后续继续研究,期待一起探讨~

本节完~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-12-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 光城 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Rust那些事之过程宏
    • 1.过程宏
      • 2.derive宏
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档