I have a struct Foo<'a>
which is a wrapper around &'a str
references. And I want to populate a HashMap
with Foo
s as keys. Here is a snippet of code (open it in playground):
use std::collections::HashMap;
#[derive(PartialEq, Eq, Hash)]
struct Foo<'a> {
txt: &'a str,
}
fn main() {
let a = "hello".to_string();
let a2 = Foo { txt: &a };
let b = "hello".to_string();
let b2 = Foo { txt: &b };
let mut hm = HashMap::<Foo, u32>::new();
hm.insert(a2, 42);
println!("=== {:?}", hm.get(&b2)); // prints Some(42)
println!("=== {:?}", hm.get_mut(&b2)); // prints Some(42)
{
let c = "hello".to_string();
let c2 = Foo { txt: &c };
println!("=== {:?}", hm.get(&c2)); // prints Some(42)
// println!("=== {:?}", hm.get_mut(&c2)); // does not compile. Why?
// hm.insert(c2, 101); // does not compile, but I understand why.
}
}
This code compiles and runs perfectly, but the compiler complains if I uncomment the two last lines of code. More precisely, it complains about the borrowed value in c2
not living long enough.
For the last one (insert
), this is perfectly understandable: I can not move c2
into the HashMap
, which lives longer than data borrowed by c2
from c
.
However, I don't understand why the second-to-last line (get_mut
) has the same problem: in that case, the borrowed data should only be necessary during the call to get_mut
, it is not moved into the HashMap
.
This is all the more surprising that the get
above works perfectly (as I expected), and that both get
and get_mut
have identical signatures when it comes to the k
parameter...
After digging a little more, I reproduced the problem with plain references (instead of a struct embedding a reference).
use std::collections::HashMap;
fn main() {
let a = 42;
let b = 42;
let mut hm = HashMap::<&u32,u32>::new();
hm.insert(&a, 13);
println!("=== {:?}", hm.get(&&b)); // prints Some(13)
println!("=== {:?}", hm.get_mut(&&b)); // prints Some(13)
{
let c = 42;
println!("=== {:?}", hm.get(&&c)); // prints Some(13)
//println!("=== {:?}", hm.get_mut(&&c)); // does not compile. Why?
}
}
Again, uncommenting the last line causes the compiler to complain (same message as above).
However, I found an interesting workaround for this particular example: replacing &&c
by &c
in the last line solves the problem -- actually, one can replace &&
by &
in all calls to get
and get_mut
. I guess this has to do with &T
implementing Borrow<T>
.
I don't understand precisely what, in this workaround, convinces the compiler to do what I want it to do. And I can not apply it directly to my original code, because I don't use references as keys, but objects embedding references, so I can not replace &&
by &
...