获取HashMap条目,如果没有则添加它

问题描述 投票:0回答:2

我想做这样的事情:

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

游乐场

如何优雅地修复它?在我看来,复制字符串似乎不是最佳选择。

rust hashmap borrow-checker
2个回答
3
投票

这里的问题是,您插入

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());

0
投票
entry(key)

需要

key
 的所有权,以便所有查找都能够创建新条目。在 
let res = match my_map.get(&key) { ... }

中,只有实际插入需要所有权。但是,当查找次数比插入次数多时,通常会使用

HashMap
。所以如果你有例如需要在堆上分配并复制的长字符串键,
get()
变体可能更便宜。
第二个问题是
or_insert_with()
不允许错误处理。如果您需要构造一些东西插入到
HashMap
中,而在构造过程中可能会返回错误,则只能更改

HashMap

来存储

Result
值——这有点尴尬。在这种情况下,
get()
可能会更简单。
作为示例,我保留了 
HashMap
用于将文件名映射到打开的文件,因此问题 1 和 2 可能都适用。
    

© www.soinside.com 2019 - 2024. All rights reserved.