🌽

Enum での 比較におけるPartialEqとmatchesの違い

2023/01/23に公開

RustのEnumで比較をする場合PartialEqを利用していましたが、matches!も比較に使えます。
比較結果が変わることがあったのでこちらで共有します。

enumの定義

最初にシンプルなenumを定義します。

enum Sports {
    BaseBall,
    BasketBall,
}

シンプルな比較

まずはそのまま比較します。 == と matches! 両方で試します。

let b1 = Sports::BaseBall;

// こちらはエラーです。
println!("{:?}", b1 == Sports::BaseBall);

// こちらは通る: true
println!("{:?}", matches!(b1, Sports::BaseBall));

== の比較を使いたい場合は、PartialEqを実装します。

#[derive(PartialEq)]
enum Sports {
    BaseBall,
    BasketBall,
}

// true
println!("{:?}", b1 == Sports::BaseBall);

// true
println!("{:?}", matches!(b1, Sports::BaseBall));

PartialEqとmatches!の違い

enumの列挙子に値を持たせる時に違いが出ます。

enum Sports {
    BaseBall(i32),
    BasketBall(f64),
}

以下のコードで試します。

fn main() {
    let b1 = Sports::BaseBall(11);

    // true
    println!("{:?}", matches!(b1, Sports::BaseBall(_)));
    
    // false
    println!("{:?}", matches!(b1, Sports::BaseBall(5)));
    
    // true
    println!("{:?}", b1 == Sports::BaseBall(11));
    
    // false
    println!("{:?}", b1 == Sports::BaseBall(5));
    
    // エラー
    println!("{:?}", b1 == Sports::BaseBall(_));
}

mathces!は列挙子の値をブランク指定すると、型の比較となりtrueになります。逆にPartialEqではブランクでの比較はビルドエラーになりました。PartialEqの方が厳密な比較ですね。

面白かったのはmatches!の方で別の値を入れると、falseになります。
気になったのでexpandしてマクロを展開してみました。

fn main() {
    let b1 = Sports::BaseBall(11);
    {
        ::std::io::_print(
            ::core::fmt::Arguments::new_v1(
                &["", "\n"],
                &[
                    ::core::fmt::ArgumentV1::new_debug(
                        &match b1 {
                            Sports::BaseBall(5) => {true},
                            _ => false,
                        },
                    ),
                ],
            ),
        );
    };
}

展開したコードを見て納得ですが、パターンマッチがSports::BaseBall(5)とマッチするかを判定していたので、これはfalseになりますね。
_がパターンの値を無視しているということが理解でき、勉強になりました。

Discussion