In Rust, i trait PartialEq ed Eq nascono dalla filosofia della sicurezza dei tipi: tutte le operazioni fondamentali per le collezioni (HashMap, HashSet) si basano su una definizione rigorosa di uguaglianza. I trait definiscono se gli oggetti di due tipi possono essere confrontati usando == e se il tipo è obbligato a implementare "uguaglianza totale" (Eq) o "uguaglianza parziale" (PartialEq).
Un'implementazione errata di PartialEq/Eq porta a un funzionamento scorretto delle collezioni, dove si violano le proprietà matematiche di base: confronto con se stessi, simmetria, transitività. Inoltre, un Eq errato può portare a oggetti che "scompaiono" nelle collezioni o a duplicati inaspettati.
PartialEq viene implementato per i tipi per i quali il confronto non è possibile per tutti i valori (ad esempio, f32, dove NaN != NaN). Eq è destinato solo a relazioni "rigide" (totali). I tipi utente devono implementare il confronto in rigorosa conformità con questi contratti.
Esempio di codice:
#[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
Caratteristiche chiave:
Perché per il tipo f32 la libreria standard implementa solo PartialEq, ma non Eq?
Perché secondo IEEE-754 NaN != NaN, cioè la proprietà x == x per tutti gli x non è rispettata, e questo è un requisito necessario per Eq.
È obbligatorio implementare Eq esplicitamente se PartialEq è stato implementato manualmente?
Sì, se il tipo supporta l'uguaglianza totale (x == x è sempre true), è necessario implementare manualmente anche Eq, altrimenti alcune strutture si rifiuteranno di compilarsi con il tuo tipo.
Può un utente implementare PartialEq "asimmetricamente" (x == y, ma y != x)?
Tecnicamente sì, ma ciò porta a un funzionamento scorretto delle collezioni e viene considerato un bug.
** Caso negativo
Un utente ha implementato PartialEq per un tipo che confronta solo un campo, ignorando gli altri. Di conseguenza, HashSet "perde" i duplicati.
Pro:
Contro:
** Caso positivo
Utilizza #[derive(PartialEq, Eq)] o implementa la propria versione del confronto, considerando tutta la logica di business, garantendo le proprietà richieste.
Pro:
Contro: