rust开发web项目附录一——简单的proc macro教程
鉴于我们需要在irmin上层编写数据管理相关的功能,而此类功能为了方便用户也为了高效性,自是绕不开proc macro,由于在下自己对proc macro亦是使用多过编写,这里便从头开始编写一篇proc macro的教程,本文主要参考的资料均为社区久负盛名的文档,如果各位观看完之后仍深感疑惑,那说明在下的水平有很大问题,与引用的资料无关(叠甲,过)
官方文档对proc macro的解释是,让你可以在编译期执行操作rust语法的代码,既可以消耗掉rust代码,也可以生成rust代码,可以理解为一个作用于AST(抽象语法树)的函数
从以上描述可以大概猜测proc macro在编译的哪一步执行(不知道的自行补充一下编译原理的知识),整体来说可以简单的理解为在编译期对代码生成的抽象语法树进行修改
可能很抽象,但是没关系,我们一点一点来了解,首先来创建一个用于学习proc macro的项目,注意要开启proc macro
然后,最基本的proc macro可以是一个简单的编译期函数(注意,此场景下不如直接编写另一种宏来的方便快捷)
proc macro 不能在创建的库使用,故而本文的测试将有单独的一个项目来负责,这里为了阅读体验就不涉及对应项目的创建了
测试一下:

那么,实际上这里的宏写起来有点像D语言的mixin,但显然如果只是用字符串的话,那可算不上方便,但我们现在还是新手,得先了解proc macro本身,之后再去进阶
那么,接下来了解重点,derive macro和attribute macro,首先还是一样的是接收Token Stream,返回TokenStream的函数
注意,这里我们只是使用derive macro创建了一个函数,并没有干别的事情,而且该函数并不会成为这个struct的成员或怎么样,只是单纯的添加了一个函数
那么,将测试修改为:
依旧会得到hello world(这里rust analyser可能会报错,但实际上并无错误)
然后是作为derive macro的一部分的attribute macro,大家多少都用过,我也就不多说了,这里直接写一下例子
这里定义了一个无意义的attribute宏,该类型的宏的主要作用是用来做标记
由于该宏没有任何意义,这里就不追加测试了(浪费键盘耐久度)
另一种attribute macro则有所不同,该macro接收两个参数一个是括号内的参数,一个是被derive的对象,该macro与derive目的一致,但效果略有不同
到这里为止,rust reference便无更多有意义的参考了,例如TokenStream是个怎样的类型(作为一个容器类型,表明上看不出具体的存储方式和可能的值),我们如何更方便的提供作为返回值的TokenStream,此时就需要参考一些其他的文档
而rust book则稍微提供了一些,之所以将这个置于rust reference之后,主要原因是该内容没有对derive macro做基本的介绍和解释,便直接开始引用syn和quote库进行编写,虽然也能行得通,但未免太过跳跃
那么,我们便引入syn和quote

那么,接下来,我们假设我们有一个trait需要通过derive宏来提供默认的基于struct本身成员的实现,但开始之前我们得先对这两个库有点基本的了解
那么,老规矩,cargo doc --open
然后我们可以看到我们此时依赖的crate之中甚至还有个proc-macro2(其实和proc-macro并非版本更新的关系)
那么从这个库的文档开始看起,首先该库是上述我们提到的两个库中syn的依赖项,该库在官方标准的proc-macro的上层,但支持从proc-macro的TokenStream到其自己的TokenStream之间相互转换,倒也不算麻烦,这里我们主要了解一下这个库提供的一些类型,例如,Ident之类的,这些对我们后续解析宏和上下文中的其他东西大有帮助

然后我来看quote,quote这个库提供了一些工具用于将rust语法转换为TokenStream,使得用户在编写derive宏的时候可以使用类似macro_rule的方式,以下是该库提供的宏和traits:

最后是syn,之所以将其放在最后,是该库依赖于上述两个库,加之其本身也比另外两个库复杂许多
但该库使用起来却并不复杂,由于其本身是TokenStream到语法树的转换库,故而其上层api主要为此服务
这里我们并不需要深入学习该库,只需要其提供的解析derive input的功能即可
首先一个常规的proc-macro也许长这样:
这里用到了DeriveInput类型,我们简单看一下这个类型定义:

此时我们可以把之前的hello_world macro修改成下面这样:
此时,该宏将为对应类型实现一个hello world trait(注意,由于proc-macro开启的库是无法声明公开的trait的,所以,我们需要定义在别的的地方,这里的实现部分在当前库没有定义trait的情况下依然可用,你自己清楚你的trait的标准即可),然后测试一下:

那么其他的macro形式也类似,再牛逼的库的derive功能也都是这些简单的宏构成的,各位多练习应当就能达成自己的使用要求了
rust reference的proc macro相关文档:https://doc.rust-lang.org/reference/procedural-macros.html#procedural-macros
rust official book的相关文档:https://doc.rust-lang.org/book/ch19-06-macros.html
最后推荐还想进一步了解的各位可以去看一下大佬的库,例如serde

