macro-rules 中的 Or 模式

摘要

  • macro_rules 宏中模式的工作方式略有变化
    • macro_rules 中的 $_:pat 现在也匹配 | 的使用,例如 A | B
    • 新的 $_:pat_param 行为与之前的 $_:pat 类似;它不匹配(顶层)|
    • $_:pat_param 在所有版本中都可用。

详情

从 Rust 1.53.0 开始,模式被扩展为支持模式中任何位置嵌套的 |。 这使您可以编写 Some(1 | 2) 而不是 Some(1) | Some(2)。由于之前不允许这样做,因此这不是一个破坏性的更改。

但是,此更改也会影响 macro_rules。此类宏可以使用 :pat 片段说明符接受模式。目前,:pat 匹配顶层 |,因为在 Rust 1.53 之前,并非所有模式(在所有嵌套级别)都可以包含 |。接受像 A | B 这样的模式的宏,例如 matches!() 使用类似于 $($_:pat)|+ 的语法。

因为这可能会破坏现有的宏,所以 :pat 的含义在 Rust 1.53.0 中没有更改为包含 |。相反,此更改发生在 Rust 2021 中。在新版本中,:pat 片段说明符匹配 A | B

Rust 2021 中的 $_:pat 片段不能后跟显式的 |。由于有时仍然希望匹配后跟 | 的模式片段,因此已添加片段说明符 :pat_param 以保留旧的行为。

重要的是要记住,版本是按 crate 划分的,因此唯一相关的版本是定义宏的 crate 的版本。使用宏的 crate 的版本不会更改宏的工作方式。

迁移

当存在 $_:pat 在 Rust 2021 中会改变含义的使用时,会触发一个 lint rust_2021_incompatible_or_patterns

您可以通过运行以下命令自动迁移您的代码以与 Rust 2021 版本兼容或确保它已经兼容

cargo fix --edition

如果您的宏依赖于 $_:pat 不匹配模式中顶层使用的 |,您需要将每次出现的 $_:pat 更改为 $_:pat_param

例如

#![allow(unused)]
fn main() {
macro_rules! my_macro { 
	($x:pat | $y:pat) => {
		// TODO: implementation
	} 
}

// This macro works in Rust 2018 since `$x:pat` does not match against `|`:
my_macro!(1 | 2);

// In Rust 2021 however, the `$_:pat` fragment matches `|` and is not allowed
// to be followed by a `|`. To make sure this macro still works in Rust 2021
// change the macro to the following:
macro_rules! my_macro { 
	($x:pat_param | $y:pat) => { // <- this line is different
		// TODO: implementation
	} 
}
}