rust办宏G什么意思_Rust过程宏入门(一)——过程宏简介

什么是宏?

熟悉C/C++的朋友应该很熟悉宏(Macro)的概念,而Rust初学者也必定会接触到Rust中的宏,其Hello world程序中就会用到println!宏。

可以简单地理解为:宏即编译时将执行的一系列指令。其重点在于「编译时」,尽管宏与函数(或方法)形似,函数是在运行时发生调用的,而宏是在编译时执行的。

不同于C/C++中的宏,Rust的宏并非简单的文本替换,而是在词法层面甚至语法树层面作替换,其功能更加强大,也更加安全。

如下所示的一个C++的宏SQR的定义

#include #define SQR(x) (x * x)int main() {

std::cout << SQR(1 + 1) << std::endl;

return 0;

}

我们希望它输出4,但很遗憾它将输出3,因为SQR(1 + 1)在预编译阶段通过文本替换展开将得到(1 + 1 * 1 + 1),并非我们所期望的语义。

而在Rust中,按如下方式定义的宏:

macro_rules!sqr{($x:expr)=>{x*x}}fn main(){println!("{}",sqr!(1+1));}

将得到正确的答案4。这是因为Rust的宏展开发生在语法分析阶段,此时编译器知道sqr!宏中的$x变量是一个表达式(用$x:expr标记),所以在展开后它知道如何正确处理,会将其展开为((1 + 1) * (1 + 1))。

由于本文介绍的重点是过程宏,因此涉及普通宏的内容便不多赘述,有兴趣者可参考官方文档上的介绍。

什么是过程宏?

过程宏(Procedure Macro)是Rust中的一种特殊形式的宏,它将提供比普通宏更强大的功能。方便起见,本文将Rust中由macro_rules!定义的宏称为规则宏以示区分。

过程宏分为三种:派生宏(Derive macro):用于结构体(struct)、枚举(enum)、联合(union)类型,可为其实现函数或特征(Trait)。

属性宏(Attribute macro):用在结构体、字段、函数等地方,为其指定属性等功能。如标准库中的#[inline]、#[derive(...)]等都是属性宏。

函数式宏(Function-like macro):用法与普通的规则宏类似,但功能更加强大,可实现任意语法树层面的转换功能。

过程宏的定义与使用方法

派生宏

派生宏的定义方法如下:

#[proc_macro_derive(Builder)]fn derive_builder(input: TokenStream)-> TokenStream{let_=input;unimplemented!()}

其使用方法如下:

#[derive(Builder)]struct Command{// ...}

属性宏

属性宏的定义方法如下:

#[proc_macro_attribute]fn sorted(args: TokenStream,input: TokenStream)-> TokenStream{let_=args;let_=input;unimplemented!()}

使用方法如下:

#[sorted]enum Letter{A,B,C,// ...}

函数式宏

函数式宏的定义方法如下:

#[proc_macro]pubfn seq(input: TokenStream)-> TokenStream{let_=input;unimplemented!()}

使用方法如下:

seq!{nin0..10{/* ... */}}

过程宏的原理

以上三种过程宏的定义方法已全部介绍。可以发现,它的定义方式与普通函数无异,只不过其函数调用发生在编译阶段而已。下面以较为常见的派生宏为例,介绍过程宏的原理。

回顾刚才的定义:

#[proc_macro_derive(Builder)]fn derive_builder(input: TokenStream)-> TokenStream{let_=input;unimplemented!()}

首先,#[proc_macro_derive(Builder)]表明derive_builder是一个派生宏,Builder表示它将作用的地方。比如定义如下结构体

#[derive(Builder)]struct Command{// ...}

就会触发以上派生宏执行。至于其中的Builder具体代表什么含义,本期暂不展开,后面再详细介绍。

fn derive_builder(input: TokenStream) -> TokenStream函数头部表明该函数将接受一个TokenStream对象作为输入,并返回一个TokenStream 对象。

要理解TokenStream,需要一些简单的编译原理知识。编译器在编译一段程序时,会首先将输入的文本转换成一系列的Token(标识符、关键字、符号、字面量等),同时忽略注释(文档注释除外)与空白字符等。

例如println!("Hello world");这句代码将被转换成标识符println 、叹号! 、圆括号( 、字面量"Hello world" 、圆括号) 、分号; 几个Token。

TokenStream顾名思义,是Rust中对一系列连续的Token的抽象。在宏展开的过程中,遇到派生宏时,会将整个结构体(或enum、union)展开成TokenStream作为派生宏函数的输入,然后将其输出的TokenStream附加到结构体后面,再继续作语法分析。

本期的介绍到此结束,主要介绍了过程宏的基本概念、定义及使用方法、实现原理。接下来将通过几个具体实例详细介绍Rust过程宏的编程方法。

实例来源自GitHub上的仓库:https://github.com/dtolnay/proc-macro-workshop/​github.com

由于作者初学Rust水平有限,如有错误,欢迎批评指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值