エラーの発生シナリオデータを保持するための新しい構造体を定義した直後、2つのインスタンスを比較しようとするとコンパイラがエラーを出すことがあります。これは通常、カスタム型に対して等価(==)または不等価(!=)演算子を使用した場合に発生します。
次のコード例を見てみましょう:
struct User {
id: u32,
username: String,
}
fn main() {
let user1 = User { id: 1, username: String::from("alice") };
let user2 = User { id: 1, username: String::from("alice") };
if user1 == user2 { // ここでコンパイラが停止します
println!("ユーザーは同一です");
}
}
cargo build を実行すると、見慣れた赤いエラーメッセージが表示されます:
error[E0369]: binary operation `==` cannot be applied to type `User`
--> src/main.rs:10:14
|
10 | if user1 == user2
| ----- ^^ ---- User
| |
| User
|
note: User に `PartialEq` の実装が不足している可能性があります
原因PythonやJavaScriptの経験がありますか?それらの言語では、実行時にオブジェクトが自動的に比較されることを期待するかもしれません。しかし、Rustは推測することを拒みます。明示性とパフォーマンスを優先するため、複雑なデータ構造をどのように比較すべきかを勝手に想定することはありません。
== 演算子を使用するには、その型が PartialEq トレイトを実装している必要があります。このトレイトは、コンパイラが等価性をチェックするために必要なロジックを提供します。これがないと、Rustはすべてのフィールドを比較すべきか、それとも特定のIDだけを比較すべきかの判断ができません。
手っ取り早い修正方法:#[derive] を使用する最も迅速な修正方法は、Rustにコードを自動生成させることです。すべてのフィールドが一致する場合にのみ2つのインスタンスを等しいとみなしたい場合は、手続き型マクロを使用します。
構造体の定義の上に #[derive(PartialEq)] を追加するだけです:
#[derive(PartialEq)]
struct User {
id: u32,
username: String,
}
fn main() {
let user1 = User { id: 1, username: String::from("alice") };
let user2 = User { id: 1, username: String::from("alice") };
assert_eq!(user1, user2); // これで正常に動作します
}
重要な要件マクロを機能させるには、構造体内のすべてのフィールドも PartialEq を実装している必要があります。u32、f32、String などの標準的な型はすでに実装されています。しかし、構造体にこのトレイトを欠いたカスタムの Address 構造体などが含まれている場合、コンパイラはその特定のフィールドが原因であることを指摘します。
詳細な修正方法:手動での実装標準的なフィールドごとの比較が常に正しいとは限りません。例えば InventoryItem において、timestamp(タイムスタンプ)や quantity(数量)が異なっていても、一意の sku さえ一致すれば等価であるとみなしたい場合があります。このような場合は、トレイトを手動で実装します。
struct InventoryItem {
sku: u32,
quantity: i32,
}
impl PartialEq for InventoryItem {
fn eq(&self, other: &Self) -> bool {
self.sku == other.sku
}
}
fn main() {
let item1 = InventoryItem { sku: 1001, quantity: 5 };
let item2 = InventoryItem { sku: 1001, quantity: 500 };
// SKUのみを考慮するため、これはtrueを返します
if item1 == item2 {
println!("同じ製品ですが、在庫数が異なります");
}
}
PartialEq vs. Eq:何が違うのか?PartialEq と並んで Eq トレイトもよく見かけるでしょう。これらは似ていますが、数学的に異なる目的を持っています:
- PartialEq: 値が自分自身と等しくない可能性がある比較を扱います。例えば、IEEE 754 浮動小数点演算では、
NaN == NaNは false になります。- Eq: これは「完全な等価性(total equality)」を示すマーカートレイトです。すべての値aに対して、a == aが常に真であることをコンパイラに伝えます。構造体が浮動小数点数(f32またはf64)を使用していない場合は、両方を derive するのがベストプラクティスです。これにより、完全な等価性を必要とするHashMapのキーとして型を使用できるようになります。
#[derive(PartialEq, Eq)]
struct Product {
id: u64,
}
修正の確認トレイトを追加したら、2つの簡単なチェックで確認しましょう。まず、cargo check を実行して E0369 エラーが消えたことを確認します。次に、ロジックが意図通りに動作することを確認するためのユニットテストを記述します。
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_equality() {
let p1 = Product { id: 500 };
let p2 = Product { id: 500 };
assert!(p1 == p2);
}
}
cargo test を実行します。緑色の「ok」メッセージが表示されれば、カスタム型は Rust の比較演算子と完全に互換性を持つようになりました。

