过程宏内幕解析「一」

3,771 阅读2分钟

你有没有想过Rust过程宏是如何工作的?此blog系列中,我们将详细介绍过程宏的一些细节。

本系列文章是在我们团队成员 Vladislav Beskrovny 最近在RustCon上做了一个关于这个话题演讲的基础上,并做了一些轻微的修改和补充。

而在这篇文章中,我们从看看Rust中宏的基础知识开始,然后进入过程宏的具体内容。

宏观下的macro

宏在Rust中随处可见,当然也有一些编程语言根本没有使用宏。让我们先看看什么是宏,以及它们带来了什么优势?宏的作用主要在三个方面:

  • 元编程 → 编写宏代码从而生成其他代码
  • 允许使用自定义的结构来扩展语言
  • 减少模板代码的数量

元编程

我们创建一个 vector 并将三个数字 push 其中:

fn main() {
   let mut a = Vec::new();
   a.push(1);
   a.push(2);
   a.push(3);
}

上述代码我们同时可以使用标准库的 vec! 来重写它:

fn main() {
   let a = vec![1, 2, 3];
}

vec![1,2,3] 是对 vec! 的调用。这个宏是一个声明宏,其声明如下(简化版本):

macro_rules! vec {
   ($($x:expr),+) => ({
       let mut v = Vec::new();
       $( v.push($x); )+
       v
   });
}

此处 $($x:expr),+ 被称为一个 宏模式。宏调用的主体是 [1,2,3],与当前宏模式匹配:

image.png

然后,元变量以如下方式被放入扩展模板中:

image.png

请注意,宏扩展代码(上图右)看起来非常像我们用于本例的初始代码。事实上,当扩展代码取代了宏调用时,它将原始代码转变为以下内容:

fn main() {
   let a = {
       let mut v = Vec::new();
       v.push(1);
       v.push(2);
       v.push(3);
       v
   };
}

然后,编译器可以像处理普通代码一样上述代码。


TODO!!!

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情