Rust 宏

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),*),这是一个重复的模式。

该模式由零个或多个以逗号分隔的表达式组成,这些表达式由宏匹配。星号(*)符号在末尾将重复匹配宏定义体内的 $() 中的模式。

Breakdown of macro match rule in Rust
Rust 中宏匹配规则的细分

花括号 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! 宏。

我们的高级学习平台,凭借十多年的经验和数千条反馈创建。

以前所未有的方式学习和提高您的编程技能。

试用 Programiz PRO
  • 交互式课程
  • 证书
  • AI 帮助
  • 2000+ 挑战