quornian
quornian

Reputation: 10153

Odd HashMap lifetime requirement on key when value is a new type wrapper

I'm trying to understand how changing the value type in a HashMap from &'t str to Value<'t>(&'t str) leads to a stricter requirement on the Key type passed in to get below.

#![allow(dead_code, unused)]
use std::collections::HashMap;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Key<'t>(&'t str);

#[derive(Debug, Clone, Copy)]
struct Value<'t>(&'t str);

#[derive(Debug)]
struct Map1<'t>(HashMap<Key<'t>, &'t str>);

#[derive(Debug)]
struct Map2<'t>(HashMap<Key<'t>, Value<'t>>);

impl<'t> Map1<'t> {
    fn get<'map>(&'map self, key: &Key<'_>) -> Option<&'map str> {
        self.0.get(key).map(|x| *x)
    }
}

impl<'t> Map2<'t> {
    fn get<'map>(&'map self, key: &Key<'_>) -> Option<&'map Value<'t>> {
        // Doesn't work, says:    -------- help: add explicit lifetime `'map` to the type of `key`: `&Key<'map>`
        self.0.get(key)
    }
}

In Map1 with value type &'t str it's fine to pass in a Key with any lifetime, whereas in Map2 with value type Value<'t> (a new type wrapper around &'t str) it is no longer fine and I'm expected to pass a key whose inner lifetime is as long as the map itself.

Could you help me understand why this the case?

Is there anything I can do to make the new type wrapped Value(&str) work the same as a &str?

Upvotes: 2

Views: 58

Answers (1)

Silly Freak
Silly Freak

Reputation: 4231

The two get implementations are not equivalent:

        self.0.get(key).map(|x| *x)
        // vs
        self.0.get(key)

what the map is doing is essentially a copy(). What would be the type of the Map1::map method without that copy?

impl<'t> Map1<'t> {
    fn get<'map>(&'map self, key: &Key<'_>) -> Option<&'map &'t str> {
        self.0.get(key)
    }
}

... which gives you the same error.

So, what you really need is to copy that Value, e.g. like this:

impl<'t> Map2<'t> {
    fn get<'map>(&'map self, key: &Key<'_>) -> Option<Value<'t>> {
        self.0.get(key).copied()
    }
}

If you want to not do that, then naturally you will need to change the lifetime declaration compared to Map1.

Upvotes: 2

Related Questions