我想做这样的事情:
fn some_fn() {
let mut my_map = HashMap::from([
(1, "1.0".to_string()),
(2, "2.0".to_string()),
]);
let key = 3;
let res = match my_map.get(&key) {
Some(child) => child,
None => {
let value = "3.0".to_string();
my_map.insert(key, value);
&value // HERE IT FAILS
}
};
println!("{}", res);
}
但编译时出现错误:
error[E0597]: `value` does not live long enough
--> src/lib.rs:16:13
|
16 | &value // HERE IT FAILS
| ^^^^^^
| |
| borrowed value does not live long enough
| borrow later used here
17 | }
| - `value` dropped here while still borrowed
error[E0382]: borrow of moved value: `value`
--> src/lib.rs:16:13
|
14 | let value = "3.0".to_string();
| ----- move occurs because `value` has type `String`, which does not implement the `Copy` trait
15 | my_map.insert(key, value);
| ----- value moved here
16 | &value // HERE IT FAILS
| ^^^^^^ value borrowed here after move
如何优雅地修复它?在我看来,复制字符串似乎不是最佳选择。
这里的问题是,您插入
value
字符串,然后尝试返回对它的引用,尽管它已经被移动到 HashMap 中。你真正想要的是插入值,然后获取对插入值的引用,它可能看起来像这样:
let res = match my_map.get(&key) {
Some(child) => child,
None => {
let value = "3.0".to_string();
my_map.insert(key, value);
my_map.get(&key).unwrap() // unwrap is guaranteed to work
}
};
但不要这样做。它很丑陋而且很慢,因为它必须在地图中查找密钥两次。 Rust 有一个方便的 Entry
类型,可让您获取 HashMap
的条目,然后对其执行操作:
// or_insert returns the value if it exists, otherwise it inserts a default and returns it
let res = my_map.entry(key).or_insert("3.0".to_string());
或者,如果生成默认值的操作成本很高,您可以使用 or_insert_with
来传递闭包:
// this won't call "3.0".to_string() unless it needs to
let res = my_map.entry(key).or_insert_with(|| "3.0".to_string());
我想稍微平衡一下已接受的答案:使用中有两个问题
let res = my_map.entry(key).or_insert_with(|| "3.0".to_string());
entry(key)
需要
key
的所有权,以便所有查找都能够创建新条目。在
let res = match my_map.get(&key) { ... }
中,只有实际插入需要所有权。但是,当查找次数比插入次数多时,通常会使用
HashMap
。所以如果你有例如需要在堆上分配并复制的长字符串键,get()
变体可能更便宜。第二个问题是or_insert_with()
不允许错误处理。如果您需要构造一些东西插入到 HashMap
中,而在构造过程中可能会返回错误,则只能更改 HashMap
来存储
Result
值——这有点尴尬。在这种情况下,get()
可能会更简单。作为示例,我保留了 HashMap
用于将文件名映射到打开的文件,因此问题 1 和 2 可能都适用。