rs.nkmk.me

Rustの==, !=演算子とeq, neメソッドおよびPartialEq, Eqトレイト

Posted: | Tags: Rust

Rustでは、==, !=演算子によって2つの値が等しいかどうかを判定できる。x == y, x != yの実体はx.eq(&y), x.ne(&y)であり、eq, neメソッドはPartialEqトレイトに含まれている。

以下のサンプルコードでは、説明のため、型推論によって省略できるときも明示的に型注釈を記述している場合がある。

また、サンプルコード中でassert!マクロを使っている。コメントアウトしていない場合、引数の式がtrueと評価されることを示している。

==, !=演算子とeq, neメソッド

==演算子は、左辺と右辺の値が等価であるとtrue、等価でないとfalseを返す。!=演算子はその逆。

let result: bool = 2 + 2 == 4;
println!("{result}");
// true

let result: bool = 2 + 2 == 5;
println!("{result}");
// false

let result: bool = 2 + 2 != 4;
println!("{result}");
// false

let result: bool = 2 + 2 != 5;
println!("{result}");
// true
source: eq_ne.rs

==, !=の実体はeq, neメソッドであり、x == y, x != yは、x.eq(&y), x.ne(&y)と書ける。

let a: i32 = 10;
let b: i32 = 10;
let c: i32 = 100;

assert!(a == b);
assert!(a.eq(&b));

assert!(a != c);
assert!(a.ne(&c));
source: eq_ne.rs

eq, neメソッドはPartialEqトレイトに含まれている。

==, !=演算子で比較できる型、できない型

A, Bの値a, bがあるとき、PartialEq<B> for Aが実装されていると、a.eq(&b), a.ne(&b)すなわちa == b, a != bの比較が可能。

PartialEqがどのような型で実装されているかは「Implementors」の節に一覧になっている。

例えばi32の場合、PartialEq<i32> for i32のみが実装されているので、i32の値同士でしか比較できない。異なる型の値との比較はコンパイルエラーになる。型を揃えれば比較可能。他の数値型も同じ。

let i_32: i32 = 100;
let i_64: i64 = 100;

// assert!(i_32 == i_64);
// assert!(i_32.eq(&i_64));
// error[E0308]: mismatched types

assert!(i_32 as i64 == i_64);
source: eq_ne.rs

一方、例えば文字列Stringと文字列スライス&strの場合、PartialEq<&str> for StringPartialEq<String> for &str(実際はライフタイム注釈が付く)が実装されているので、互いに比較可能。

let s_str: &str = "abc";
let s_string: String = String::from("abc");

assert!(s_str == s_string);
assert!(s_str.eq(&s_string));

assert!(s_string == s_str);
assert!(s_string.eq(&s_str));
source: eq_ne.rs

また、ベクタVec<T>と配列[T; N]の場合、PartialEq<[U; N]> for Vec<T, A>は実装されているが逆は実装されていないので、順番によってはコンパイルエラーとなる。

let v: Vec<i32> = vec![10, 20, 30];
let a: [i32; 3] = [10, 20, 30];

assert!(v == a);
assert!(v.eq(&a));

// assert!(a == v);
// assert!(a.eq(&v));
// error[E0277]: can't compare `[i32; 3]` with `std::vec::Vec<i32>`
source: eq_ne.rs

PartialEq, Eqトレイト

PartialEqトレイトは対称および推移の特性を保証する。

例えば、型Aの値a1, a2, a3があるとき、PartialEq<A> for Aが実装されていると以下の特性が成り立つ。

  • 対称(Symmetric): a1 == a2ならばa2 == a1
  • 推移(Transitive): a1 == a2, a2 == a3ならばa1 == a3

Eqトレイトは対称、推移に加えて、反射の特性を保証する。

  • 反射(Reflexive): a1 == a1

Eqトレイトはメソッドを持たず、その特性を保証するマーカートレイトとして使われる。例えばHashMap<K, V>のキーKEqを実装している必要がある。

例えば、浮動小数点数f32, f64には非数NaNが含まれる。NaN != NaNであるため、f32, f64にはPartialEqは実装されているが、Eqは実装されていない(実装できない)。したがって、f32, f64HashMapのキーにすることはできない。

PartialEqの特性についての補足

PartialEqの対称および推移の特性を、異なる型の値も含めて一般化すると以下のようになる。

  • 対称(Symmetric)
    • A, Bの値a, bがあるとき、PartialEq<B> for A, PartialEq<A> for Bが実装されていると、a == bならばb == a
  • 対称性(Symmetric)
    • A, B, Cの値a, b, cがあるとき、PartialEq<B> for A, PartialEq<C> for B, PartialEq<C> for Aが実装されていると、a == b, b == cならばa == c

The equality relation == must satisfy the following conditions (for all a, b, c of type A, B, C):

  • Symmetric: if A: PartialEq<B> and B: PartialEq<A>, then a == b implies b == a; and
  • Transitive: if A: PartialEq<B> and B: PartialEq<C> and A: PartialEq<C>, then a == b and b == c implies a == c.

Note that the B: PartialEq<A> (symmetric) and A: PartialEq<C> (transitive) impls are not forced to exist, but these requirements apply whenever they do exist. PartialEq in std::cmp - Rust

最後に注釈がついているが、ベクタVec<T>と配列[T; N]の例のように、PartialEq<B> for Aが実装されているときに必ずPartialEq<A> for Bが実装されているとは限らないので注意。

関連カテゴリー