本教程解释了函数和类函数宏之间的区别。例如,为什么 msg! 后面会有一个感叹号?本教程将解释这种语法。
作为一种强类型语言,Rust 的函数不能接受任意数量的参数。
例如,Python 的 print 函数可以接受任意数量的参数:
print(1)
print(1, 2)
print(1, 2, 3)
! 表示该“函数”是一个类函数宏。
Rust 的类函数宏通过 ! 符号的存在来标识,例如 println!(...) 或 Solana 中的 msg!(...)。
在 Rust 中,用于打印内容的常规函数(不是类函数宏)是 std::io::stdout().write,它只接受单个字节字符串作为参数。
如果你想运行以下代码,又不想搭建开发环境,Rust Playground 是一个很方便的工具。
让我们看看以下示例(摘自此处):
use std::io::Write;
fn main() {
std::io::stdout().write(b"Hello, world!\n").unwrap();
}
请注意,write 是一个函数,而不是一个宏,因为它没有 !。
如果你尝试像前面在 Python 中那样做,代码将无法编译,因为 write 只接受一个参数:
// this does not compile
use std::io::Write;
fn main() {
std::io::stdout().write(b"1\n").unwrap();
std::io::stdout().write(b"1", b"2\n").unwrap();
std::io::stdout().write(b"1", b"2", b"3\n").unwrap();
}
因此,如果你希望打印任意数量的参数,你需要编写自定义的 print 函数,来为每种不同数量参数的情况提供处理逻辑 —— 这效率极低!
这类代码看起来会是这样(强烈不推荐这样做!):
use std::io::Write;
// print one argument
fn print1(arg1: &[u8]) -> () {
std::io::stdout().write(arg1).unwrap();
}
// print two arguments
fn print2(arg1: &[u8], arg2: &[u8]) -> () {
let combined_vec = [arg1, b" ", arg2].concat();
let combined_slice = combined_vec.as_slice();
std::io::stdout().write(combined_slice).unwrap();
}
// print three arguments
fn print3(arg1: &[u8], arg2: &[u8], arg3: &[u8]) -> () {
let combined_vec = [arg1, b" ", arg2, b" ", arg3].concat();
let combined_slice = combined_vec.as_slice();
std::io::stdout().write(combined_slice).unwrap();
}
fn main() {
print1(b"1\n");
print2(b"1", b"2\n");
print3(b"1", b"2", b"3\n");
}
如果我们寻找 print1、print2 和 print3 函数的模式,会发现它仅仅是将参数插入到一个向量(vector)中,并在它们之间添加空格,然后将向量转换回字节字符串(准确地说是字节切片)。
如果我们能获取一段像 println! 这样的代码,并自动将其扩展为一个能够接受正好满足我们所需数量参数的 print 函数,那岂不是很好?
这正是 Rust 宏的作用。
Rust 宏以 Rust 代码为输入,并以编程方式将其扩展为更多 Rust 代码。
这帮助我们避免了为代码中所需的每种 print 语句去编写 print 函数的枯燥工作。
展开宏
如果你想看看 Rust 编译器如何展开 println! 宏的示例,请查阅 cargo expand github 仓库。输出结果非常冗长,因此我们在这里不作展示。
将宏视为黑盒也是可以的
当宏由库提供时,它们非常方便;但是手动编写宏非常繁琐,因为它实际上需要解析 Rust 代码。
Rust 中不同类型的宏
我们提供的 println! 示例是一个类函数宏。Rust 还有其他类型的宏,但我们关心的另外两种是自定义派生宏(custom derive macro)和类属性宏(attribute-like macro)。
让我们看一个由 Anchor 创建的全新程序:

我们将在后续教程中解释它们的工作原理。
通过 RareSkills 了解更多
本教程是我们免费 Solana 课程 的一部分。
原载于 2024 年 2 月 15 日