跳到主要内容

Rust宏

提示
  1. 宏的基本概念:Rust中的宏是一种元编程工具,允许编写生成更多代码的代码,常用于简化重复模式,如println!vec!panic!
  2. 创建和使用宏:使用macro_rules!宏创建新宏,定义具体规则和模式。例如,创建无参数的宏hello_world,用于生成特定输出。
  3. 宏的参数和重复:宏可以接受参数并根据输入定制生成代码,如print_message宏。使用$(...)*语法在宏中生成重复代码,灵活处理多个输入。

Rust 中的宏是一段生成另一段代码的代码。

宏根据输入生成代码,简化重复模式,并使代码更简洁。

Rust 宏简单地让我们编写生成更多代码的代码,这也被称为元编程。宏在 Rust 中被广泛使用。

一些流行的 Rust 宏包括 println!vec!panic!

在 Rust 中创建宏

我们可以使用 macro_rules! 宏来创建宏。或许令人惊讶的是,我们确实使用一个宏来创建宏。

macro_rules! 宏有一个特殊的语法。

macro_rules! macro_name {
(...) => {...}
// 更多匹配规则
}

这里,() => {} 是宏规则的条目。我们可以在一个宏中匹配许多规则。

让我们看一个简单的宏示例,它定义了一个新函数来打印 “Hello, World!”。

// 一个名为 `hello_world` 的简单宏
macro_rules! hello_world {
// `()` 表示宏不接受参数
() => {
// 宏将展开为这个块的内容
println!("Hello, World!")
};
}

fn main() {
// 调用 hello_world 宏
// 这个调用将展开为 `println!("Hello, World!");`
hello_world!()
}

输出

Hello, World!

在这个示例中,我们创建了一个名为 hello_world 的宏。宏定义有一个匹配规则:

() => {
println!("Hello, World!");
};

要调用宏,我们在 main() 函数中使用 hello_world!() 调用。

宏会将 hello_world!() 调用替换为宏定义中的代码,即 println!("Hello, World!")

在 Rust 中创建带参数的宏

宏也可以接受参数,这允许我们根据不同的输入定制它生成的代码。

例如,这里有一个定义打印自定义消息的函数的宏:

// 一个名为 `print_message` 的宏
macro_rules! print_message {
// 接受一个参数表达式的匹配规则
($message:expr) => {
println!("{}", $message)
};
}

fn main() {
// 使用参数调用宏
print_message!("I am learning Rust!");
}

输出

我正在学习 Rust

这里,我们创建了一个名为 print_message 的宏,它接受一个参数 $message。宏的参数以美元符号 $ 开头,并使用指示器进行类型注解。

Rust 会尝试匹配定义在匹配规则中的模式。在上面的例子中,我们的规则是:

($message:expr) => {
println!("{}", $message)
};

美元符号 $ 后的第一部分是变量的名称。我们将其捕获为 $message

分号 : 之后的部分称为指示器,它们是我们可以选择匹配的类型。在这个示例中,我们使用的是表达式指示器(expr)。

现在,当我们调用宏 print_message!("我正在学习 Rust!") 时,它会匹配我们的输入表达式并捕获 $message 变量。

这里,$message 被赋值为 "我正在学习 Rust!",然后传递给 println!("{}", $message)

它会生成等同于写 println!("{}", "我正在学习 Rust!") 的代码。$message 参数允许我们指定要打印的消息。

注意:在宏规则体内我们可以使用许多指示器:

  • stmt:一条语句
  • pat:一个模式
  • expr:一个表达式
  • ty:一个类型
  • ident:一个标识符

Rust 中的宏重复

Rust 宏在我们需要生成重复代码时也很有用。我们可以定义一个宏来接受参数,并根据这些参数重复生成代码。

macro_rules! 宏通过 $(...)* 语法支持重复。括号内的 ... 可以是任何有效的 Rust 表达式或模式。

以下是演示宏重复的示例:

// 一个使用重复的宏
macro_rules! repeat_print {
// 匹配规则,匹配参数中的多个表达式
($($x:expr),*) => {
$(
println!("{}", $x);
)*
};
}

fn main() {
// 使用多个参数调用宏
repeat_print!(1, 2, 3);
}

输出

1
2
3

这里,宏 repeat_print 接受单个参数 ($($x:expr),*),这是一个重复模式。

模式由零个或多个用逗号分隔的表达式组成,由宏匹配。结尾的星号(*)将反复匹配 $() 内的模式。

Rust 中宏匹配规则的分解

花括号内的代码 println!("{}", $x);,被零次或多次重复,对于宏定义体内 $(...)* 包围的参数列表中的每个表达式都执行一次。代码中的 $x 指的是匹配的表达式。

生成的代码的每次迭代都将打印不同的表达式。现在,当我们调用 repeat_print!(1, 2, 3); 时,宏将生成以下代码:

println!("{}", 1); // 匹配参数 1,
println!("{}", 2); // 匹配参数 2,
println!("{}", 3); // 匹配参数 3

因此,这个宏 repeat_print! 可以以简洁便利的方式打印多个表达式,无需每次都写出 println! 宏。