To use custom structures in collections like HashMap or HashSet, it is necessary to implement the traits Eq and Hash (often also PartialEq). The implementation of these traits defines how objects are compared to each other and how their hash is computed.
Importantly: if two objects are equal (a == b), then their hash values must match (hash(a) == hash(b)). Otherwise, containers that depend on hashes will not function correctly (it will be impossible to find or remove an element).
Example implementation:
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); } }
Can you implement Hash for a structure where some fields participate in equality comparison (Eq/PartialEq), and some only in hashing?
Answer:
No, this will lead to an invariant violation: if two elements are considered equal, their hash must be the same. All fields involved in PartialEq/Eq must also be considered in Hash. Ignoring "non-comparable" fields is safe only if they do not participate in the comparison logic.
impl Hash for MyType { fn hash<H: Hasher>(&self, state: &mut H) { self.x.hash(state); // if only self.x is used in Eq } }
Story
In a large server project, a structure for a key was used in
HashMap, but one field participating inPartialEqwas forgotten in hashing. As a result, the inability to find the required key led to a "leak" of users from the cache: elements were considered identical, although they were different.
Story
In an open-source library for geometric objects, floating-point comparison (
f64) was used inHashandEq. As a result, due to the peculiarities of comparing NaN and -0.0/+0.0, standard containers worked incorrectly, sometimes not finding an existing object in the collection.
Story
In one fintech project, during refactoring, auto-derivation
#[derive(Hash, Eq, PartialEq)]was used for a structure with private fields, changing the order of their declaration. As a result, the final hash changed, which broke the caching mechanism and caused data loss after a restart.