Rust の matches! マクロ — match 式への糖衣で bool を返すパターン判定
TL;DR
matches!(expr, pattern)は「式がパターンにマッチするか」をboolで返す std マクロ(Rust 1.42 から prelude 入り、import 不要)。- 実体は match 式への薄い糖衣。マッチしたら
true、それ以外(_)はfalseに展開される。 - パターン構文は match アームと完全に同じ。
|による or パターン、範囲パターン、ifガード(パターンで束縛した名前を参照可能)が使える。 - 値の取り出しはできない(bool のみ)。値が欲しいなら
if let/matchを使う。 - テストでの検証には
assert_matches!が望ましいとされるが nightly 限定(#82775)のため、stable ではassert!(matches!(...))で代用。
このドキュメントの射程
matches!マクロの定義・展開結果・返り値・使いどころの整理。- 前提知識として、マクロ呼び出しの
!とmacro_rules!の基本(matcher / transcriber、メタ変数、繰り返し)にも触れる。 - 環境: Rust 1.42+(stable)。
assert_matches!の節のみ nightly。
前提: ! と macro_rules! の基本
println!やmatches!の!は「関数ではなくマクロ呼び出し」のマーカー。マクロはコンパイル時にコードへ展開される。- マクロ自体に「戻り値」の概念はなく、展開された構文がその場に置かれるだけ。式に展開されるマクロなら、その式の評価結果が実質の返り値になる(
vec![1, 2]→Vecを作る式、matches!→boolを返す match 式)。 macro_rules!の各ルールは matcher(マッチする構文)と transcriber(置き換え後の構文)の組。ルールは上から順に試され、最初に成功したものが転写される。- メタ変数はフラグメント指定子付きで書く(
$x:expr、$p:pat_paramなど)。繰り返しは$(…)+*/+/?(例:$( $i:ident ),*はカンマ区切りの識別子列)。
matches! の定義と展開
std での定義(matcher 部分):
macro_rules! matches {
($expression:expr, $(|)? $($pattern:pat_param)|+ $(if $guard:expr)? $(,)?) => { ... };
}
読み方:
$expression:expr— 判定対象の式。$(|)?— 先頭の|を任意で許容(match アームで先頭|が書けるのと同じ)。$($pattern:pat_param)|+—|区切りで 1 個以上のパターン。pat_paramは top-level の|を含まないパターン(|はマクロ側の区切りとして扱うため)。$(if $guard:expr)?— 任意の if ガード。$(,)?— 末尾カンマ許容。
展開結果のイメージ:
match $expression {
$( $pattern )|+ $( if $guard )? => true,
_ => false,
}
つまり 返り値は常に bool。
使用例
// or パターン + 範囲パターン
let foo = 'f';
assert!(matches!(foo, 'A'..='Z' | 'a'..='z'));
// if ガード(パターンで束縛した x を参照できる)
let bar = Some(4);
assert!(matches!(bar, Some(x) if x > 2));
// enum の is_xxx メソッドの定番実装
enum Foo { A, B(T) }
impl Foo {
fn is_b(&self) -> bool {
matches!(self, Foo::B(_))
}
}
// イテレータの filter とも相性が良い
let evens_or_none: Vec<_> = vec![Some(1), None, Some(2)]
.into_iter()
.filter(|v| matches!(v, Some(n) if n % 2 == 0) || matches!(v, None))
.collect();
できないこと:
// NG: matches! は bool を返すだけで、束縛した値を外に出せない
// let x = matches!(bar, Some(x)); // x は外で使えない
// 値を取り出したいなら if let / let else / match を使う
if let Some(x) = bar {
println!("{x}");
}
テストでの使い分け
- マッチの検証には、失敗時に値の Debug 表現を出力してくれる
assert_matches!が一般に望ましいとされる。 - ただし
assert_matches!は nightly 限定の実験的 API(tracking issue #82775,#![feature(assert_matches)]が必要)。 - stable では
assert!(matches!(value, pattern))で代用する(失敗時に値は表示されない点だけ劣る)。
まとめ
matches!は match 式への糖衣で、返り値は常にbool。判定だけならmatches!、値の取り出しはif let/match、と使い分ける。