2

我开始了学习 Rust 的旅程。我在Rust by Example中遇到了这一行

但是,与 C 和其他语言中的宏不同,Rust 宏被扩展为抽象语法树,而不是字符串预处理,因此您不会遇到意外的优先级错误。

为什么抽象语法树比字符串预处理更好?

4

2 回答 2

15

如果你在 C 中有这个:

#define X(A,B) A+B
int r = X(1,2) * 3;

的值r将是7,因为预处理器将其扩展为1+2 * 3,即1+(2*3)

在 Rust 中,您将拥有:

macro_rules! X { ($a:expr,$b:expr) => { $a+$b } }
let r = X!(1,2) * 3;

这将评估为9,因为编译器会将扩展解释为(1+2)*3。这是因为编译器知道宏的结果应该是一个完整的、自包含的表达式。

也就是说,C 宏也可以这样定义:

#define X(A,B) ((A)+(B))

这将避免任何不明显的评估问题,包括由于上下文而重新解释的论点本身。但是,当您使用宏时,您永远无法确定宏是否正确解释了它可能使用的所有可能方式,因此很难判断任何给定的宏扩展会做什么。

通过使用 AST 节点而不是文本,Rust 确保不会发生这种歧义。

于 2018-05-24T14:48:58.460 回答
6

使用 C 预处理器的经典​​示例是

#define MUL(a, b) a * b

// ...

int res = MUL(x + y, 5);

宏的使用将扩展到

int res = x + y * 5;

这与预期相差甚远

int res = (x + y) * 5;

发生这种情况是因为 C 预处理器实际上只是进行简单的基于文本的替换,它并不是语言本身的真正组成部分。预处理和解析是两个独立的步骤。

如果预处理器像编译器的其余部分一样解析宏,这发生在宏是实际语言语法的一部分的语言中,这不再是一个问题,因为优先级(如前所述)和关联性被考虑在内。

于 2018-05-24T14:45:23.640 回答