マクロ規則における 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: 実装 } } }