Hash
The std::hash::Hash trait feeds the bytes and structure of a value into a stateful Hasher so the value can be used as a key in hash-based collections such as HashMap<K, V> and HashSet<T>.
Trait Definition
pub trait Hash {
fn hash<H: Hasher>(&self, state: &mut H);
fn hash_slice<H: Hasher>(data: &[Self], state: &mut H)
where
Self: Sized,
{
for piece in data {
piece.hash(state);
}
}
}
Hash separates what data is hashed (Hash::hash, implemented by the key type) from how bytes are mixed into a 64-bit integer (Hasher, implemented by the hashing algorithm).
Key Value (e.g., UserId)
│
▼ calls .hash(&mut hasher)
+------------------------+
| H: Hasher (SipHash13) | <-- feeds fields sequentially
+------------------------+
│
▼ calls hasher.finish()
u64 Hash Digest (e.g., 0x9f4a_2c81_03b7_e512)
Deriving Hash
Applying #[derive(PartialEq, Eq, Hash)] generates a Hash implementation that feeds each field (and enum discriminant) into the Hasher in declaration order:
use std::collections::HashSet;
#[derive(Debug, PartialEq, Eq, Hash)]
struct Endpoint {
host: String,
port: u16,
}
fn main() {
let mut visited = HashSet::new();
visited.insert(Endpoint {
host: String::from("example.com"),
port: 443,
});
let query = Endpoint {
host: String::from("example.com"),
port: 443,
};
println!("Contains endpoint: {}", visited.contains(&query));
}
Contains endpoint: true
The Eq and Hash Consistency Contract
Every type used as a HashMap or HashSet key must uphold the fundamental invariant:
$$\text{If } a == b\text{, then } \text{hash}(a) == \text{hash}(b)$$
If you implement PartialEq manually to ignore a field (such as a timestamp or cached value), you must also implement Hash manually and omit that exact same field. Deriving Hash while manually implementing PartialEq (Cold/Clippy lint derived_hash_with_manual_eq) breaks HashMap lookups because equal keys produce different hashes:
use std::collections::HashSet;
use std::hash::{Hash, Hasher};
struct Account {
id: u64,
display_name: String,
}
impl PartialEq for Account {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for Account {}
impl Hash for Account {
fn hash<H: Hasher>(&self, state: &mut H) {
// Must hash ONLY `id` to match `PartialEq`:
self.id.hash(state);
}
}
fn main() {
let mut set = HashSet::new();
set.insert(Account {
id: 7,
display_name: String::from("Alice"),
});
let lookup = Account {
id: 7,
display_name: String::from("Alice (Renamed)"),
};
println!("Found account 7: {}", set.contains(&lookup));
}
Found account 7: true