我有一个名为
这段代码可以编译和运行得很好,但是如果我取消最后两行的注释,编译器就会抱怨
struct Foo<'a>
的结构体,它是对&'a str
引用的封装。我想将Foo
作为键填充到HashMap
中。以下是代码片段(在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.
}
}
这段代码可以编译和运行得很好,但是如果我取消最后两行的注释,编译器就会抱怨
c2
中借用的值不够长久。对于最后一个(insert
),这是完全可以理解的:我不能将c2
移动到比从c
中借用的数据更长寿的HashMap
中。然而,我不明白为什么倒数第二行(get_mut
)也有同样的问题:在这种情况下,借用的数据应该只在调用get_mut
时需要,它没有被移动到HashMap
中。更令人惊讶的是,上面的get
正常工作(正如我所期望的那样),而且当涉及k
参数时,get
和get_mut
具有相同的签名。经过深入挖掘,我用普通引用(而不是嵌入引用的结构体)重现了这个问题。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?
}
}
再次取消注释最后一行会导致编译器抱怨(与上面相同的消息)。
然而,我发现了一个有趣的解决方法:将最后一行中的&&c
替换为&c
可以解决问题——实际上,在所有调用get
和get_mut
时都可以用&
代替&&
。我想这与&T
实现Borrow<T>
有关。
我不明确地理解,在这个解决方法中,是什么使得编译器按照我的意愿执行。我无法直接将其应用于我的原始代码,因为我不使用引用作为键,而是嵌入引用的对象,所以我无法用&
代替&&
...
get_mut
不会将参数存储在HashMap
中呢? - Shepmaster&self
和&mut self
的区别。由于有了生命周期省略,该方法为get_mut<'a, Q: ?Sized>(&'a mut self, k: &Q) -> Option<&'a mut V>
— 生命周期是相关联的。 - Shepmaster&T
在T
中是变体的,&mut T
在T
中是不变的,而HashMap<K, V>
在K
中是变体的。祝你好运! - trent