Accepting a HashMap with any hasher as a function param

I'm extremely new to Rust and I'm trying to build a first crate! I am writing a function that takes a HashMap as its argument. I have it working, but Clippy is barking at me saying that I should be mindful of callers that may want to provide a different build hasher. I can't seem to find the best way to do this. I was able to get the warning to go away by just mandating that RandomState must be the third parameter:

pub fn load(env_vars: &HashMap<&str, Option<&str>, RandomState>) -> ... { }

This works and there's no warning. It's good that it's no longer implicit, but this didn't really solve any problem besides making the limitation explicit.

I imagine this would be a wonderful opportunity to learn from the Clippy docs, but they are severely lacking in providing breadcrumbs on how one would resolve this issue.
I tried doing some reading about BuildHasherDefault, although I admittedly had some trouble making the connection. I naively tried this:

pub fn load<H>(env_vars: &HashMap<&str, Option<&str>, BuildHasherDefault<H>>) -> ... { }

Although this is valid, the calling code no longer works and I think I ran into a dead end on this line. I was hoping to have RandomState be the "default" if possible and allow it to be overridden, but those may touch on concepts I have not learned yet.

I think you should just be able to do this:

pub fn load<H>(env_vars: &HashMap<&str, Option<&str>, H>)
1 Like
use std::collections::HashMap;
use std::hash::BuildHasher;

pub fn load<H: BuildHasher>(env_vars: &HashMap<&str, Option<&str>, H>) {
    env_vars.get("i");
}

fn main() {
    load(&HashMap::new())
}

playground

1 Like

OK now I feel stupid. That seemed too easy and it seems to have worked so far. I have been working with scripting languages for far too long and maybe forgot how simple generics can be used.

So this is working because HashMap knows what a generic in position 3 must satisfy?

Yes, because HashMap is defined as

pub struct HashMap<K, V, S = RandomState> { /* fields omitted */ }

So it knows that S may be omitted and that it has the default of RandomState if it is omitted.

Awesome! I haven't really gotten to generics much yet until now so I stumbled a bit. Thank you :slight_smile:

I can see how the relationship works now

1 Like

You can see in the HashMap documentation three impl blocks:

impl<K: Hash + Eq, V> HashMap<K, V, RandomState>

impl<K, V, S> HashMap<K, V, S>

impl<K, V, S> HashMap<K, V, S> where
    K: Eq + Hash,
    S: BuildHasher, 

For functions in the second block, you can see the hasher used doesn't actually matter. I'm guessing you're only using the iterator functions on the HashMap? Those are in the second block. For the other accessors on the map, you need to do what @RustyYato suggested.

1 Like

Ahhh. OK I will give it a more thorough read. Your first answer with the new context seems like it is totally sufficient then, as you are correct and I am only iterating.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.