Rust的宏

介绍

宏在类C语言中好处是很多的,特别是对于工程比较大的适合,业务比较复杂或者代码优美的情况下会使用。

Rust 宏非常不同于 C 里面的宏。Rust 宏会被应用于词法树(token tree),而 C 语言里的宏则是文本替换。

Rust中的宏参数可以使用 ()[] 以及 {},虽然三种使用形式皆可,但是 Rust 内置的宏都有自己约定俗成的使用方式,例如 vec![...]assert_eq!(...) 等。

在 Rust 中宏分为两大类:声明式宏( declarative macros ) macro_rules! 和三种过程宏( procedural macros ):

  • 声明式宏(Declarative macros)使得你能够写出类似 match 表达式的东西,来操作你所提供的 Rust 代码。它使用你提供的代码来生成用于替换宏调用的代码。

  • 过程宏(Procedural macros)允许你操作给定 Rust 代码的抽象语法树(abstract syntax tree, AST)。过程宏是从一个(或者两个)TokenStream到另一个TokenStream的函数,用输出的结果来替换宏调用。

  • #[derive],在之前多次见到的派生宏,可以为目标结构体或枚举派生指定的代码,例如 Debug trait

  • 类属性宏(Attribute-like macro),用于为目标添加自定义的属性

  • 类函数宏(Function-like macro),看上去就像是函数调用

宏特点:

  • 元编程:左右就是复用代码,类似用宏来生成代码。

宏会被展开成其它代码,且这个展开过程是发生在编译器对代码进行解释之前。因此,宏可以为指定的类型实现某个特征:先将宏展开成实现特征的代码后,再被编译。

  • 函数就做不到这一点,因为它直到运行时才能被调用,而特征需要在编译期被实现。

  • 宏可以引入可变参数:println!("hello"),println!("hello {}", name),而函数参数是固定的,不同JS/TS。

声明式宏

( $( $x:expr ),* )

使用圆括号 () 将整个宏模式包裹其中。紧随其后的是 $(),跟括号中模式相匹配的值(传入的 Rust 源代码)会被捕获,然后用于代码替换。在这里,模式 $x:expr 会匹配任何 Rust 表达式并给予该模式一个名称:$x

$() 之后的逗号说明在 $() 所匹配的代码的后面会有一个可选的逗号分隔符,紧随逗号之后的 * 说明 * 之前的模式会被匹配零次或任意多次(类似正则表达式)。

使用 vec![1, 2, 3] 来调用该宏时,$x 模式将被匹配三次,分别是 123

  1. $() 中包含的是模式 $x:expr,该模式中的 expr 表示会匹配任何 Rust 表达式,并给予该模式一个名称 $x

  2. 因此 $x 模式可以跟整数 1 进行匹配,也可以跟字符串 "hello" 进行匹配: vec!["hello", "world"]

  3. $() 之后的逗号,意味着12 之间可以使用逗号进行分割,也意味着 3 既可以没有逗号,也可以有逗号:vec![1, 2, 3,]

  4. * 说明之前的模式可以出现零次也可以任意次,这里出现了三次

过程宏procedural macros

从形式上来看,过程宏跟函数较为相像,但过程宏是使用源代码作为输入参数,基于代码进行一系列操作后,再输出一段全新的代码。注意,过程宏中的 derive 宏输出的代码并不会替换之前的代码,这一点与声明宏有很大的不同。

  1. 属性式宏(Attribute-like macros)

  2. 派生宏(Derive macros)

  3. 函数式宏(Function-like macros)

cargo new macro_demo --lib ,在cargo.toml中新增[lib]\n proc-macro = true

[dependencies]\n syn = {version="1.0.57",features=["full","fold"]} quote = "1.0.8"

lib同级目录新建tests文件夹,下面新建attribute_macro.rs文件,然后cargo test

Rust 中的自定义派生宏能够对 trait 进行自动实现。这些宏通过使用#[derive(Trait)]自动实现 trait。

要想在 Rust 中写一个自定义派生宏,我们可以使用DeriveInput来解析派生宏的输入。我们还将使用proc_macro_derive宏来定义一个自定义派生宏。

参考:Rust宏:教程与示例(一)Rust宏:教程与示例(二)

Last updated