Rust中宏和函数有什么区别?

61

引用自Rust博客

最后一个要提到的是:如果你用过C语言的宏,Rust的宏有很大的不同。

Rust中的宏和函数有什么区别?与C语言有何不同?

3个回答

57

继续阅读文档,特别是有关宏的章节

Rust函数与Rust宏

宏在编译时执行。它们通常扩展为新的代码片段,然后编译器需要进一步处理。

Rust宏与C宏

对我来说最大的区别是Rust宏是卫生的。该书有一个例子说明了卫生的作用,并且还说:

每个宏扩展发生在单独的“语法上下文”中,并且每个变量都标记有其引入的语法上下文。

它使用了这个例子:

例如,这个C程序输出13而不是预期的25。

#define FIVE_TIMES(x) 5 * x

int main() {
    printf("%d\n", FIVE_TIMES(2 + 3));
    return 0;
}

除此之外, Rust 宏

  • 可以随编译代码一起分发
  • 可以根据参数数量进行重载
  • 可以匹配语法模式,如大括号、圆括号或逗号
  • 可以要求重复输入模式
  • 可以递归使用
  • 在语法级别上操作,而不是文本级别上

28
这个答案缺少回答“宏和函数的区别是什么?”所需的一件事是“我们为什么需要函数?”,因为该答案似乎将宏描述为具有所有函数功能及更多功能。 - mcarton
16
我不理解为什么是5 * 2 + 3而不是5 * 5。值得一提的是,由于大多数人可能不会阅读链接,因此 FIVE_TIMES(x) 将把 x 视为操作 2+3而不是 2+3 = 5 的结果。将2+3代入公式,得到5*2+3,按照运算顺序,首先计算5*2=10,然后加上3得到13。因此,如果你从字符串的角度来看,这只是一个未求值的替换。 - oooyaya
1
@ mcarton,大概的答案应该是您应该更喜欢函数,并仅在不可避免的情况下使用宏(例如可变参数)。原因在于“宏定义更加复杂”,因此“通常更难阅读”,正如Rust文档和Vahin Sharma的回答所解释的那样。 - Tom Bailey
1
这就是为什么在C语言的宏定义中使用额外的括号是一个好习惯: #define FIVE_TIMES(x) (5 * (x)) - Aron Insinga
很遗憾,Rust 的宏不像 Nim 中的宏/模板那样具有类型安全性。 - Alexandre Daubricourt

32
引用自Rust文档
宏和函数的区别在于,宏本质上是编写生成其他代码的代码的一种方式,这被称为元编程。在附录C中,我们讨论了派生属性(derive attribute),该属性会为您生成各种特性的实现。我们还在整本书中使用了println!和vec!宏。所有这些宏都会扩展以生成比手动编写的代码更多的代码。
元编程有助于减少您必须编写和维护的代码量,这也是函数的作用之一。但是,宏具有一些函数没有的额外功能。
函数签名必须声明函数拥有的参数数量和类型。另一方面,宏可以接受可变数量的参数:我们可以使用一个参数调用println!(“hello”),或者使用两个参数调用println!(“hello {}”,name)。此外,宏在编译器解释代码含义之前进行扩展,因此宏可以例如在给定类型上实现特性。函数无法实现这一点,因为它在运行时被调用,而特性需要在编译时实现。
与函数相比,实现宏而不是函数的缺点在于,宏定义比函数定义更复杂,因为您正在编写编写Rust代码的Rust代码。由于这种间接性,宏定义通常比函数定义更难阅读、理解和维护。
宏和函数之间的另一个重要区别是,您必须在文件中定义宏或将其引入作用域中,然后才能调用它们,而函数可以在任何地方定义并调用。

2
好的。但为什么? - Philippe Fanaro

3
在宏中,您可以使用可变数量的参数。
在函数中,您必须定义参数的数量和类型。

13
请说明这个答案在现有的提到“可以在参数数量上进行重载”的回答中增加了什么内容。 - Shepmaster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接