Pour utiliser des structures personnalisées dans des collections comme HashMap ou HashSet, il est nécessaire d'implémenter les traits Eq et Hash (souvent aussi PartialEq). L'implémentation de ces traits définit comment les objets sont comparés entre eux et comment leur hachage est calculé.
Important : si deux objets sont égaux (a == b), leurs valeurs de hachage doivent coïncider (hash(a) == hash(b)). Sinon, les conteneurs qui dépendent des hachages fonctionneront mal (il sera impossible de trouver ou de supprimer un élément).
Exemple d'implémentation :
use std::hash::{Hash, Hasher}; #[derive(Eq, PartialEq)] struct Point { x: i32, y: i32, } impl Hash for Point { fn hash<H: Hasher>(&self, state: &mut H) { self.x.hash(state); self.y.hash(state); } }
Est-il possible d'implémenter Hash pour une structure où certaines champs participent à la comparaison d'égalité (Eq/PartialEq), tandis que d'autres ne sont utilisées que pour le hachage ?
Réponse :
Non, cela conduira à violer l'invariant : si deux éléments sont considérés comme égaux, leur hachage doit être identique. Tous les champs impliqués dans PartialEq/Eq doivent également être pris en compte dans Hash. Ignorer les champs "non comparables" est sûr uniquement s'ils ne participent pas à la logique de comparaison.
impl Hash for MyType { fn hash<H: Hasher>(&self, state: &mut H) { self.x.hash(state); // seulement si self.x est utilisé également dans Eq } }
Histoire
Dans un grand projet de serveur, une structure pour la clé a été utilisée dans
HashMap, mais lors du hachage, un champ participant àPartialEqa été oublié. Par conséquent, l'impossibilité de trouver la clé souhaitée a entraîné une "fuite" d'utilisateurs du cache : les éléments étaient considérés comme identiques, bien qu'ils soient différents.
Histoire
Dans une bibliothèque open source pour des objets géométriques, une comparaison à virgule flottante (
f64) a été utilisée dansHashetEq. En conséquence, en raison des particularités de la comparaison NaN et -0.0/+0.0, les conteneurs standards fonctionnaient avec des erreurs, ne trouvant parfois pas un objet existant dans la collection.
Histoire
Dans un projet fintech, lors d'une refactorisation, l'auto-derivation
#[derive(Hash, Eq, PartialEq)]a été utilisée pour une structure avec des champs privés, modifiant l'ordre de leur déclaration. En conséquence, le hachage final a changé, ce qui a brisé le fonctionnement du cache et a entraîné des pertes de données après un redémarrage.