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