HashMap with_hasher. In Rust we can specify a hash function for the HashMap (and HashSet) structs. By doing this, we can improve performance for programs that use hashtable lookups.
To specify a custom Hasher, we use with_hasher (or with_hasher_and_capacity). Then, we use the HashMap like we normally do. The HashMap has a different type (the Hasher is the third generic type).
Example. This program uses 2 different Hasher arguments to create HashMaps. It accesses the Hasher from the standard library, and then uses an external crate (ahash).
Part 1 We use the with_hasher function to create a HashMap. We use the Hasher from the standard library—this is the same as calling new().
Part 2 Ahash is an optimized hash function in an external crate (the ahash crate). We must add the dependency with cargo first.
use std::collections::*;
fn main() {
// Part 1: use HashMap with default hasher from standard library.
let mut h = HashMap::with_hasher(std::hash::RandomState::default());
h.insert("bird", 10);
if let Some(result) = h.get("bird") {
println!("{result}");
}
// Part 2: use HashMap with ahash Hasher (add ahash dependency first).
let mut h = HashMap::with_hasher(ahash::RandomState::default());
h.insert("bird", 10);
if let Some(result) = h.get("bird") {
println!("{result}");
}
}10
10
Type example. When we create a HashMap with a custom Hasher (using with_hasher), the type of the HashMap is affected. The custom Hasher type is the third generic type of a HashMap.
Tip It is possible to use a type alias for your custom hasher, and use that throughout your program for your HashMap types.
use std::collections::*;
// Part 1: the type of a HashMap with a custom hasher has 3 generic arguments.
struct Example {
h: HashMap<String, usize, ahash::RandomState>,
}
fn main() {
// Part 2: create the HashMap.
let mut h = HashMap::with_hasher(ahash::RandomState::default());
h.insert("cat".to_string(), 100usize);
// Part 3: store the HashMap within another struct.
let example = Example { h };
println!("{}", example.h.len());
}1
Benchmark. It it worth the effort to replace the default hash function in Rust programs? This benchmark aims to determine if the "ahash" crate is worth using.
Version 1 We create a HashMap with the default hasher—this could just be a call to new().
Version 2 We use the ahash Hasher, which is also called RandomState. We perform the same lookups in both versions of the code.
Result For just doing lookups, the ahash crate's Hasher is many times faster than the default Hasher in Rust as of 2024.
use std::collections::*;
use std::time::*;
fn main() {
let mut map = HashMap::with_hasher(std::hash::RandomState::default());
map.insert("bird", 0);
map.insert("frog", 0);
map.insert("dog", 0);
let mut map2 = HashMap::with_hasher(ahash::RandomState::default());
map2.insert("bird", 0);
map2.insert("frog", 0);
map2.insert("dog", 0);
if let Ok(max) = "100000000".parse::<usize>() {
let mut count = 0;
// Version 1: use HashMap with standard library hasher.
let t0 = Instant::now();
for _ in 0..max {
if let Some(_) = map.get("frog") {
count += 1;
}
}
println!("{} ms", t0.elapsed().as_millis());
// Version 2: use HashMap with ahash hasher (from crate).
let t1 = Instant::now();
for _ in 0..max {
if let Some(_) = map2.get("frog") {
count += 1;
}
}
println!("{} ms", t1.elapsed().as_millis());
println!("{}", count);
}
}779 ms
102 ms
200000000
Summary. HashMaps and HashSets in Rust are well-optimized, but with a custom Hasher from a crate such as Ahash we can achieve even higher performance. For some programs this is a big speedup.
Dot Net Perls is a collection of tested code examples. Pages are continually updated to stay current, with code correctness a top priority.
Sam Allen is passionate about computer languages. In the past, his work has been recommended by Apple and Microsoft and he has studied computers at a selective university in the United States.