Passing custom hasher to HashMap


#1

I want to try custom hash functions from the fasthash crate, but find myself unable to construct the HashMap with the correct arguments.

extern crate fasthash;

use self::fasthash::CityHasher;
use std::collections::HashMap;

let hasher = CityHasher::default();

let mut hmap: HashMap<u8, u8>;
hmap = HashMap::with_hasher(hasher);

This results in the error:

12 |     hmap = HashMap::with_hasher(hasher);
   |                                 ^^^^^^ expected struct `std::collections::hash_map::RandomState`, found struct `request_cache::fasthash::CityHasher64`
   |
   = note: expected type `std::collections::hash_map::RandomState`
              found type `request_cache::fasthash::CityHasher64`

So from what I gather, HashMap wants a RandomState object, but I don’t know how to give it that, combined with the custom hasher.

The fasthash documentation gives the following example:

use std::hash::{Hash, Hasher};
use std::collections::HashMap;
use fasthash::RandomState;
use fasthash::city::CityHash64;
let s = RandomState::<CityHash64>::new();
let mut map = HashMap::with_hasher(s);

But that fails too (I gave the HashMap <u8, u8> parameters) with:

expected struct `std::collections::hash_map::RandomState`, found struct `request_cache::fasthash::RandomState`

And hash_map::RandomState doesn’t accept construction parameters such as CityHash64.

Any suggestions welcome, also on how I can better figure these things out myself in the future. I chased down various structs and trait-methods in the documentation, but didn’t gain much insight as to how i could accomplish this task.


#2

If you look at https://doc.rust-lang.org/std/collections/struct.HashMap.html you can see that `HashMap| has three parameters, K, V, and S. The problem is that the third one has a default value, which is the default hasher. You need to specify the hasher you want to use when you define the type of your map.


#3

HashMap takes a 3rd type parameter, S, which is of type BuildHasher (that’s a trait that defines how to make Hasher instances). HashMap also defaults this type parameter to be RandomState; if you don’t specify a different hash builder, then you get this one.

std defines a BuildHasherDefault struct, which implements BuildHasher and hence can be used as that S type parameter for a HashMap. BuildHasherDefault itself is generic on a type H. When H implements Default and Hasher, then BuildHasherDefault<H> implements BuildHasher, and that’s the type you want for HashMap.

So, the following should work (CityHasher, which is a type alias for CityHasher64, implements Default and Hasher):

   let mut hmap = HashMap::<u8, u8, BuildHasherDefault<CityHasher>>::default();

Alternatively,

   let mut hmap = HashMap::with_hasher(BuildHasherDefault<CityHasher>{});

However, I’d just use the default() based approach.


#4

Thanks a lot, that did it! I even got as far as trying BuildHasherDefault, but I never would have guessed to invoke ::default() there. I hope I can develop an intuition for this over time (where to look, what to expect is missing etc.), the way one might for the C++ Standard Library.

p.s. Should I mark this solved (edit the title accordingly), or leave it as is?


#5

You will. It’ll come with practice/experience.

No need - leave as-is (this forum is more discussion oriented and not as structured as, say, stackoverflow). At least that’s my understanding.