Rust 中的宏是一段生成另一段代码的代码。
宏根据输入生成代码,简化重复模式,并使代码更简洁。
Rust 宏允许我们编写生成更多代码的代码,这也称为元编程。宏在 Rust 中被广泛使用。
一些流行的 Rust 宏包括 println!
、vec!
和 panic!
。
在 Rust 中创建宏
我们可以使用 macro_rules!
宏来创建宏。这可能令人惊讶,但我们确实使用宏来创建宏。
macro_rules!
宏具有特殊的语法。
macro_rules! macro_name {
(...) => {...}
// more match rules
}
这里,() => {}
是宏规则的入口。我们可以在一个宏中拥有许多规则来匹配。
让我们看一个简单的宏示例,该宏定义了一个新的函数来打印“Hello, World!”。
// A simple macro named `hello_world`
macro_rules! hello_world {
// `()` indicates that the macro takes no argument
() => {
// The macro will expand into the contents of this block
println!("Hello, World!")
};
}
fn main() {
// Call the hello_world macro
// This call will expand into `println!("Hello, World!");`
hello_world!()
}
输出
Hello, World!
在此示例中,我们创建了一个名为 hello_world
的宏。宏定义有一条要匹配的规则,即
() => {
println!("Hello, World!");
};
要调用宏,我们在 main()
函数中使用 hello_world!()
调用。
宏会将 hello_world!()
调用替换为宏定义中定义的代码,即 println!("Hello, World!)
。
在 Rust 中创建带参数的宏
宏还可以接受参数,这允许我们根据不同的输入自定义它生成的代码。
例如,这是一个定义打印自定义消息的函数的宏
// A macro named `print_message`
macro_rules! print_message {
// Match rule that takes an argument expression
($message:expr) => {
println!("{}", $message)
};
}
fn main() {
// Call the macro with an argument
print_message!("I am learning Rust!");
}
输出
I am learning Rust!
这里,我们创建了一个名为 print_message
的宏,它接受一个参数 $message
。宏的参数前面加上美元符号 $
,并使用 **标识符** 进行类型注解。
Rust 将尝试匹配匹配规则中定义的模式。在上例中,我们的规则是
($message:expr) => {
println!("{}", $message)
};
美元符号 $
后面的第一部分是变量名。我们将其捕获为 $message
。
分号 :
后面的部分称为标识符,这是我们可以选择匹配的类型。我们在示例中使用表达式标识符(expr
)。
现在,当我们调用宏 print_message!("I am learning Rust!")
时,它会匹配我们的输入表达式并捕获 $message
变量。
在这里,$message
被赋值为 "I am learning Rust!"
,然后传递到 println!("{}", $message)
。
它将生成等同于编写 println!("{}", "I am learning Rust!")
的代码。$message
参数允许我们指定要打印的消息。
注意:宏规则体内部可以使用许多标识符
stmt
:一个语句pat
:一个模式expr
:一个表达式ty
:一个类型ident
:一个标识符- …
Rust 中的宏重复
当我们需要生成重复代码时,Rust 宏也很有用。我们可以定义一个宏来接受参数,并根据这些参数重复生成的代码。
macro_rules!
宏支持使用 $(...)*
语法进行重复。括号内的 ...
可以是任何有效的 Rust 表达式或模式。
下面是一个演示宏重复的示例
// A macro which uses repetitions
macro_rules! repeat_print {
// match rule which matches multiple expressions in an argument
($($x:expr),*) => {
$(
println!("{}", $x);
)*
};
}
fn main() {
// Call the macro with multiple arguments
repeat_print!(1, 2, 3);
}
输出
1 2 3
这里,宏 repeat_print
接受一个参数 ($($x:expr),*)
,这是一个重复的模式。
该模式由零个或多个以逗号分隔的表达式组成,这些表达式由宏匹配。星号(*
)符号在末尾将重复匹配宏定义体内的 $()
中的模式。

花括号 println!("{}", $x);
内的代码将根据 $(...)*
在宏定义体中的包装,对列表中的每个表达式重复零次或多次。代码中的 $x
指的是匹配的表达式。
生成的代码的每次迭代将打印一个不同的表达式。现在,当我们调用 repeat_print!(1, 2, 3);
时,宏将生成此代码
println!("{}", 1); // matches argument 1,
println!("{}", 2); // matches argument 2,
println!("{}", 3); // matches argument 3
因此,该宏 repeat_print!
可以以简洁方便的方式打印多个表达式,而无需每次都写出 println!
宏。