Newtype pattern to override Eq impls for HashMap meets a dead end

Motivation

Override default Eq impl for specific type when using HashMap.

Suppose we have struct Foo: Eq + Hash whose default Eq impl is undesirable. Foo is expensive and not Copy-able.

Path to the problem

Newtype pattern

We define a wrapper type struct Wrapper<Foo>(Foo): Eq + Hash and use HashMap<Wrapper<Foo>, Bar>

Wrapping HashMap::remove

Now we wish to provide a wrapped version of the HashMap::remove and have the following signature, to allow us just to use this new HashMap like before.

fn wrapped_remove(hash_map: &HashMap<Wrapper<Foo>, Bar>, foo: &Foo) -> Option<Bar> {
    hash_map.remove(todo!("We dont have a &Wrapper(*foo)"))
}

Instantly we found the type incompatibility.

Type converting for HashMap::remove

According to HashMap's signature, we just need to have a type Q which

  1. Wrapper<Foo>: Borrow<Q>
  2. Q: Eq + Hash
  3. Q can be constructed from &Foo

Suppose we have a second generic type to serve as Q. Let's call it struct WrappedRef<'a, Foo>(&'a Foo) : Eq + Hash

Then we need to have

impl Borrow<WrappedRef<'a, Foo>> for Wrapper<Foo> {
    fn borrow<'b>(&'b self) -> &'b WrappedRef<'a, Foo> {
        todo!("Lifetime error and temporary reference cannot be returned error")
    }
}

Instantly we found the lifetime problem. We can't add the lifetime bound anywhere in the impl otherwise it breaks Borrow's signature.

Question

  1. Is there a way to implement this wrapped_remove interface as is, in order to hide this layer of newtype abstraction?
  2. Is there other way to override Eq impls for HashMaps, other than the newtype pattern?

You could

#[repr(transparent)]
#[derive(Hash, Eq, PartialEq)]
struct Wrapper(Foo);

impl AsRef<Wrapper> for Foo {
    fn as_ref(&self) -> &Wrapper {
        // SAFETY: repr(transparent)
        unsafe {&* (self as *const Foo as *const Wrapper) }
    }
}

fn wrapped_remove(hash_map: &mut HashMap<Wrapper, Bar>, foo: &Foo) -> Option<Bar> {
    hash_map.remove(foo.as_ref())
}

This is actually an elegant solution to the original problem. Although I felt std::collections::HashMap's type gynmastics falls short here.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.