Rubber ducking generics and traits

After fighting with the compiler for a bit my trait and impl have now this form:

trait HashSetExt<T> {
    fn get_item<Q>(&mut self, item: &Q) -> T
    where
    T: Default + Borrow<Q> + Eq + Hash,
    Q: Hash + Eq + ?Sized;
}

impl<T> HashSetExt<T> for HashSet<T> {
    fn get_item<Q>(&mut self, item: &Q) -> T
    where
    T: Default + Borrow<Q> + Eq + Hash,
    Q: Hash + Eq + ?Sized {
        if self.contains(item) {
            self.take(item).unwrap()
        } else {
            Default::default()
        }
    }
}

I mostly get this but something is telling me that there is probably a better / simpler way to write this trait. Could you rubber-duck this for me? :smiley:

I'm especially wary of why the HashSetExt must have the bound on T and why this has to match with the generic on HashSet.

I'm also a bit confused about the position of the where clause in the impl block because I would have expected that to be before the { like in the blanket implementations?

You can put some of the bounds on T at the impl level where clause

trait HashSetExt<T> {
    fn get_item<Q>(&mut self, item: &Q) -> T
    where
    T: Borrow<Q>,
    Q: Hash + Eq + ?Sized;
}

impl<T> HashSetExt<T> for HashSet<T> 
where T: Default + Eq + Hash {
    fn get_item<Q>(&mut self, item: &Q) -> T
    where
    Q: Hash + Eq + ?Sized, T: Borrow<Q> {
         self.take(item).unwrap_or_default()
    }
}

But I would advise against implementing this as an extension trait, you can just use set.take().unwrap_or_default() or wrap the set in a new type if you don't need it to be generic.

1 Like