New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HashMap struct key with 'static
lifetime screws with return lifetime using methods like .get()
#124614
Comments
.get()
'static
lifetime screws with return lifetime using methods like .get()
I simplified the example a bit, nethier use std::collections::HashMap;
fn resolve<'this, 'a, T>(types: &'this HashMap<&'static str, T>, key: &'a str) -> Option<&'this T> {
types.get(&key)
} |
Duplicate of: #80389 (comment) however the practical example here is amenable to a concrete possible fix, hence I'm expanding on the explanation of how to arrive there.
The standard library provides:
Fixing this is possible by ensuring the #[derive(Hash, PartialEq, Eq)]
struct Key<'lt> {
inner: &'lt str,
}
/// Wraps key so it can be `Borrow`'d under any short lifetime.
///
/// Avoid the conflicting implementation problem of using the same type.
#[derive(Hash, PartialEq, Eq)]
#[repr(transparent)]
struct KeyCapsule<'lt> {
inner: Key<'lt>,
}
// Hash, PartialEq, Eq, are compatible due to the way single-attribute structs are derived.
// It just calls the methods on each field.
impl<'a, 'b: 'a> core::borrow::Borrow<Key<'a>> for KeyCapsule<'b> {
fn borrow(&self) -> &Key<'a> {
&self.inner
}
}
fn with<'this, 'a>(map: &std::collections::HashSet<KeyCapsule<'this>>, key: &'a str) -> bool {
map.contains(&Key {
inner: key,
})
}
fn main() {
let mut map: std::collections::HashSet::<KeyCapsule<'static>>
= Default::default();
map.insert(KeyCapsule { inner: Key { inner: "Hello" }});
assert!(with(&map, "Hello"));
} |
Thank you for the very insightful response @HeroicKatora! As I switched to using // Define a key type to insert into a hashbrown::HashMap
#[derive(Clone, Debug, PartialEq, Eq)]
struct Key {
a: String,
b: String,
}
impl core::hash::Hash for Key {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
hash_key(self.a, &self.b, state);
}
}
fn hash_key<H: core::hash::Hasher>(a: &str, b: &str, state: &mut H) {
a.hash(state);
b.hash(state);
}
// And then, we can lookup using the raw_entry API like:
fn lookup<'map>(
a: &str,
b: &str,
map: &'map HashMap<Key, Info>,
) -> Option<(&'map Key, &'map Info)> {
// Manually construct the key hash
let hash = {
use core::hash::{BuildHasher, Hasher};
let mut state = map.hasher().build_hasher();
hash_key(a, b, &mut state);
state.finish()
};
// Look up the entry by hash, and then compare keys we get back to find the matching key.
map.raw_entry().from_hash(hash, |k| k.a == a && k.b == b)
} Though at some point I might go back to using Happy to see this closed anyways now. Thanks! |
I'm not sure what words to use to actually describe (or search for a dupe) this issue, so I'll just post it here and hope that it makes more sense to you guys (and sorry if it is a dupe; I'm almost sure it would be)!
So basically, if I write this code:
I get back this error:
Despite the fact that
HashMap::get()
returns the values with a lifetime of self, and nothing to do with the key provided.However, this does work:
Removing the
struct
indirection when defining the key type seems to make things happy!Background
The above is a minimal reproduction of the issue I ran into, but the reason I ran into it is because I want to have something like:
In other words, I want to be able to borrow more than 1 value to lookup some owned key in the hashmap. I went down the route of trying
struct Key<'a> { a: Cow<'a,str>, b: Cow<'a,str>
and thenHashMap<Key<'static>, String>
, but this led me to bumping up against this odd lifetime issue :)Any pointers would be amazingly welcome!
The text was updated successfully, but these errors were encountered: