In Rust ergeben sich die Traits PartialEq und Eq aus der Philosophie der Typensicherheit – alle grundlegenden Operationen für Sammlungen (HashMap, HashSet) basieren auf einer strengen Definition von Gleichheit. Die Traits definieren, ob Objekte zweier Typen mit == verglichen werden können und ob der Typ verpflichtet ist, "vollständige" Gleichheit (Eq) oder "teilweise" Gleichheit (PartialEq) zu implementieren.
Eine falsche Implementierung von PartialEq/Eq führt zu inkorrektem Verhalten von Sammlungen, bei dem grundlegende mathematische Eigenschaften verletzt werden: Vergleich mit sich selbst, Symmetrie, Transitivität. Darüber hinaus kann ein inkorrektes Eq dazu führen, dass Objekte in Sammlungen "verloren gehen" oder unerwartete Duplikate auftreten.
PartialEq wird für Typen implementiert, bei denen ein Vergleich nicht für alle Werte möglich ist (z.B. f32, wo NaN != NaN). Eq ist nur für "strenge" (totale) Beziehungen gedacht. Benutzerdefinierte Typen müssen den Vergleich in strikter Übereinstimmung mit diesen Verträgen implementieren.
Beispielcode:
#[derive(PartialEq, Eq)] struct Point { x: i32, y: i32, } let p1 = Point { x: 1, y: 2 }; let p2 = Point { x: 1, y: 2 }; assert!(p1 == p2); // true
Schlüsselfeatures:
Warum implementiert die Standardbibliothek für den Typ f32 nur PartialEq, aber nicht Eq?
Weil gemäß IEEE-754 NaN != NaN, d.h. die Eigenschaft x == x für alle x nicht eingehalten wird, und dies ist eine notwendige Eigenschaft für Eq.
Muss Eq explizit implementiert werden, wenn PartialEq manuell implementiert wurde?
Ja, wenn der Typ vollständige Gleichheit unterstützt (x == x ist immer true), muss auch Eq manuell implementiert werden, andernfalls lehnen einige Strukturen die Kompilierung mit Ihrem Typ ab.
Kann ein Benutzer PartialEq "unsymmetrisch" implementieren (x == y, aber y != x)?
Technisch ja, aber das führt zu inkorrektem Verhalten von Sammlungen und wird als Fehler angesehen.
** Negativer Fall
Ein Benutzer hat PartialEq für einen Typ implementiert, der nur nach einem Feld vergleicht, während die anderen ignoriert werden. Infolgedessen "verliert" HashSet Duplikate.
Vorteile:
Nachteile:
** Positiver Fall
Verwendet #[derive(PartialEq, Eq)] oder implementiert eine eigene Version des Vergleichs, die alle Geschäftslogiken berücksichtigt und die erforderlichen Eigenschaften garantiert.
Vorteile:
Nachteile: