capypad
0 day streak
rust / intermediate
Snippet

HashMap with Custom Key Types: Implementing Eq and Hash

To use a custom type as a HashMap key, you must implement three traits: PartialEq for equality comparison, Eq to assert transitive equality (required when PartialEq is implemented), and Hash to generate a hash value for the hasher. The hash should combine all fields that factor into equality—if two Person structs are equal, they must hash the same. This pattern enables struct-based keys instead of just primitive types.

snippet.rs
rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::fmt;
 
#[derive(Debug)]
struct Person {
name: String,
age: u8,
}
 
impl PartialEq for Person {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.age == other.age
}
}
 
impl Eq for Person {}
 
impl Hash for Person {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.age.hash(state);
}
}
 
impl fmt::Display for Person {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} ({})", self.name, self.age)
}
}
 
fn main() {
let mut people: HashMap<Person, &str> = HashMap::new();
let alice = Person { name: String::from("Alice"), age: 30 };
let bob = Person { name: String::from("Bob"), age: 25 };
people.insert(alice, "Engineer");
people.insert(bob, "Designer");
let lookup = Person { name: String::from("Alice"), age: 30 };
if let Some(&profession) = people.get(&lookup) {
println!("Alice is an {}", profession);
}
println!("Total people: {}", people.len());
}
Breakdown
1
impl PartialEq for Person {
Defines equality based on both name and age fields
2
impl Eq for Person {}
Eq is a marker trait asserting no NaN-like edge cases
3
self.name.hash(state); self.age.hash(state);
Both fields contribute to hash—order matters for consistency
4
people.get(&lookup)
Creating a new Person with same values successfully looks up the entry