I have a struct Foo<'a> which is a wrapper around &'a str references. And I want to populate a HashMap with Foos 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 &...