Generic type parameters error when wrapping HashMap

I'm trying to write a type that wraps HashMap, and am trying to write a remove method that maintains the generality of HashMap::remove. I'm running into issues on this minimum reproduction

use std::{borrow::Borrow, collections::HashMap, hash::Hash};

struct MyStruct<K, V> {
    map: HashMap<K, usize>,
    data: Vec<(K, V)>,
}

impl<K, V> MyStruct<K, V>
where
    K: Eq + Hash,
{
    pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
    where
        K: Borrow<Q>,
        Q: Hash + Eq + ?Sized,
    {
        self.map.remove(key).map(|idx| {
            let (some_key, some_val) = self.data.remove(idx);
            let _ = self.map.get(&some_key);
            some_val
        })
    }
}

I get this error

error[E0308]: mismatched types
   --> src/main.rs:19:34
    |
8   | impl<K, V> MyStruct<K, V>
    |      - found type parameter
...
12  |     pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
    |                   - expected type parameter
...
19  |             let _ = self.map.get(&some_key);
    |                              --- ^^^^^^^^^ expected `&Q`, found `&K`
    |                              |
    |                              arguments to this method are incorrect
    |
    = note: expected reference `&Q`
               found reference `&K`
    = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound
    = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
note: method defined here
   --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/collections/hash/map.rs:877:12
    |
877 |     pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
    |            ^^^

What am I doing wrong? What I don't understand is if I get rid of the type parameter Q, and make key: &K, there are no issues. This means I'm literally passing &K when the definition of HashMap::get expects &Q and I would expect the same error as above.

Any help would be greatly appreciated!

1 Like

Why are you doing let _ = self.map.get(&some_key); in the first place? That line effectively accomplishes nothing.

This is a minimum example to reproduce the error I'm getting which I don't understand.
I cut down a large chunk of code which does accomplish something for it to be digestible while still raising the same error.

for some reason, the compiler cannot unify the type K (which is a generic parameter of the impl block) and Q: ?Sized (which is a generic parameter of the HashMap::get() method).

I don't know why the inference fails, but you can use the turbofish to workaround it:

let _ = self.map.get::<K>(&some_key);
4 Likes

It also works after adding a K: Borrow<K> bound to either the trait or the method. Some bug or limitation in the compiler must be making it quit too early in its search for Borrow<K> impls.

This is fallout from preferring explicit bounds when trait-solving, I'm pretty sure. Probably someone could find an existing issue about it.

Here is one of the relevant issues: Trait impls from where clauses (`ParamEnv`) take precedence over freestanding trait impls · Issue #24066 · rust-lang/rust · GitHub

2 Likes