Macros 为什么基于抽象语法树的宏优于基于字符串预处理的宏?

Macros 为什么基于抽象语法树的宏优于基于字符串预处理的宏?,macros,rust,rust-macros,Macros,Rust,Rust Macros,我正在开始我的学习之旅。我发现: 然而,与C和其他语言中的宏不同,Rust宏被扩展为抽象语法树,而不是字符串预处理,因此不会出现意外的优先级错误 为什么抽象语法树比字符串预处理更好?使用C预处理器的一个经典示例是 #定义MUL(a,b)a*b // ... int res=MUL(x+y,5); 宏的使用范围将扩展到 int res=x+y*5; 这与预期相差甚远 intres=(x+y)*5; 发生这种情况是因为C预处理器实际上只是进行简单的基于文本的替换,它并不是语言本身不可分割的一部

我正在开始我的学习之旅。我发现:

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


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

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

#定义MUL(a,b)a*b
// ...
int res=MUL(x+y,5);
宏的使用范围将扩展到

int res=x+y*5;
这与预期相差甚远

intres=(x+y)*5;
发生这种情况是因为C预处理器实际上只是进行简单的基于文本的替换,它并不是语言本身不可分割的一部分。预处理和解析是两个独立的步骤

如果预处理器像编译器的其他部分一样解析宏,而宏是实际语言语法的一部分,则这不再是一个问题,因为考虑了优先级(如上所述)和关联性等因素。

如果在C中有这样的情况:

定义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宏也可以这样定义:

定义X(A,B)((A)+(B)) 这将避免任何不明显的评估问题,包括由于上下文而重新解释的参数本身。但是,当您使用宏时,您永远无法确定宏是否正确地考虑了所有可能的使用方式,因此很难判断任何给定的宏扩展将执行什么操作


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

因此您不会遇到意外的优先级错误感谢您提供这两种类型宏的具体示例。