マクロ規則における OR パターン

概要

  • macro_rules におけるパターンの挙動がほんの少し変更されました:
    • macro_rules において、$_:pat| を使ったパターンにもマッチするようになりました。例えば、A | B にマッチします。
    • 新しく導入された $_:pat_param は、かつての $_:pat と同じ挙動を再現します。すなわち、こちらは(トップレベルの)| にはマッチしません。
    • $_:pat_param は全てのエディションで使用可能です。

詳細

Rust 1.53.0 から、パターン中のどこでも、| をネストして使えるようになりました。 これにより、Some(1) | Some(2) でなく Some(1 | 2) と書くことができるようになりました。 今まではこうは書けなかったので、これは破壊的変更ではありません。

ところが、この変更は macro_rules で定義されたマクロ にも影響します。 macro_rules では、:pat というフラグメント指定子で、パターンを受け付けることができます。 現在のところ、:pat はトップレベルの | にマッチしません。 なぜなら Rust 1.53 以前は、全てのパターンが(どのネストレベルにでも)| を含むことができるわけではなかったからです。 matches!() のように、 A | B のようなパターンを受け付けるマクロを書くには、 $($_:pat)|+ のような書き方をしなくてはなりませんでした。

既存のマクロを壊す可能性があるため、Rust 1.53.0 では :pat| を含むことができるようには変更されませんでした。 代わりに、Rust 2021 で変更がなされました。 新しいエディションでは、:pat フラグメント指定子は A | B にマッチします

Rust 2021 では、$_:pat フラグメントに | そのものを続けることはできません。 パターンフラグメントに | が続いてるものにマッチさせたいような場合は、新しく追加された :pat_param が過去と同じ挙動を示すようになっています。

ただし、エディションはクレートごとに設定されることに注意してください。 つまり、マクロが定義されているクレートのエディションだけが関係します。 マクロを使用する方のクレートのエディションは、マクロの挙動に影響しません。

移行

$_:pat が使われている場所のうち、Rust 2021 で意味が変わるようなものに対しては、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
		// TODO: 実装
	} 
}

// This macro works in Rust 2018 since `$x:pat` does not match against `|`:
// Rust 2018 では、`$x:pat` が `|` にマッチしないので、以下のマクロは正常に動きます:
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:
// 一方 Rust 2021 では、`$_:pat` フラグメントは `|` にもマッチし、
// `|` が続くのは許されなくなりました。
// Rust 2021 でもマクロが動作するためには、マクロを以下のように変更しなくてはなりません:
macro_rules! my_macro { 
	($x:pat_param | $y:pat) => { // <- this line is different
	                             //    この行を変えた
		// TODO: implementation
		// TODO: 実装
	} 
}
}