Para utilizar estructuras personalizadas en colecciones como HashMap o HashSet, es necesario implementar los rasgos Eq y Hash (y a menudo también PartialEq). La implementación de estos rasgos determina cómo se comparan los objetos entre sí y cómo se calcula su hash.
Es importante: si dos objetos son iguales (a == b), sus valores hash deben coincidir (hash(a) == hash(b)). De lo contrario, los contenedores que dependen de hashes funcionarán incorrectamente (no se podrá encontrar o eliminar un elemento).
Ejemplo de implementación:
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); } }
¿Se puede implementar Hash para una estructura en la que algunas de las propiedades participan en la comparación de igualdad (Eq/PartialEq), mientras que otras sólo en el hash?
Respuesta:
No, esto llevará a una violación del invariantes: si dos elementos se consideran iguales, su hash debe ser necesariamente el mismo. Todos los campos que participan en PartialEq/Eq también deben ser considerados en Hash. Ignorar los campos "no comparables" es seguro solo si no participan en la lógica de comparación.
impl Hash for MyType { fn hash<H: Hasher>(&self, state: &mut H) { self.x.hash(state); // si solo self.x se usa también en Eq } }
Historia
En un gran proyecto de servidor, se utilizó una estructura para la clave en
HashMap, pero al hacer el hash se olvidó un campo que participaba enPartialEq. Como resultado, la imposibilidad de encontrar la clave correcta llevó a una "fuga" de usuarios del caché: los elementos se consideraban iguales, aunque eran diferentes.
Historia
En una biblioteca de código abierto para objetos geométricos, en
HashyEqse utilizó comparación de puntos flotantes (f64). Como resultado, debido a las peculiaridades de la comparación de NaN y -0.0/+0.0, los contenedores estándar funcionaban incorrectamente, a veces sin encontrar un objeto existente en la colección.
Historia
En un proyecto fintech, durante la refactorización, se utilizó la auto-derivación
#[derive(Hash, Eq, PartialEq)]para una estructura con campos privados, cambiando el orden de su declaración. Como resultado, el hash final cambió, lo que rompió la funcionalidad de caché y causó pérdida de datos tras el reinicio.