【问题标题】:Insert into a HashMap based on another value in the same Hashmap根据同一个 Hashmap 中的另一个值插入 HashMap
【发布时间】:2020-05-07 19:23:17
【问题描述】:

我正在尝试根据同一个 HashMap 中的另一个值向 HashMap 中插入一个值,如下所示:

use std::collections::HashMap;

fn main() {
    let mut some_map = HashMap::new();
    some_map.insert("a", 1);

    let some_val = some_map.get("a").unwrap();

    if *some_val != 2 {
        some_map.insert("b", *some_val);
    }
}

给出这个警告:

warning: cannot borrow `some_map` as mutable because it is also borrowed as immutable
  --> src/main.rs:10:9
   |
7  |     let some_val = some_map.get("a").unwrap();
   |                    -------- immutable borrow occurs here
...
10 |         some_map.insert("b", *some_val);
   |         ^^^^^^^^             --------- immutable borrow later used here
   |         |
   |         mutable borrow occurs here
   |
   = note: `#[warn(mutable_borrow_reservation_conflict)]` on by default
   = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future
   = note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159>

如果我尝试更新现有值,我可以使用内部突变和 RefCell,如 here 所述。

如果我尝试插入根据自身更新值,我可以使用入口API,如here所述。

我可以解决克隆问题,但我宁愿避免这种情况,因为在我的实际代码中检索到的值有些复杂。这需要不安全的代码吗?

【问题讨论】:

  • 你在最后一行克隆了some_val,所以你最好早点这样做——应该没什么区别。

标签: rust borrow-checker


【解决方案1】:

编辑 由于先前的答案完全是错误的并且根本没有回答问题,因此有代码没有显示任何警告(playground

现在它是一个带有Rc&lt;_&gt; 值的哈希图,val_rc 仅包含一个关于实际数据的引用计数器(在本例中为编号1)。由于它只是一个计数器,因此没有克隆它的成本。但请注意,数字只存在一个副本,因此如果您修改some_map["a"] 的值,那么some_map["b"] 也会被修改,因为它们指的是一块内存。另请注意,1 存在于堆栈中,因此如果您打算添加许多重物,最好考虑将其转换为 Rc&lt;Box&lt;_&gt;&gt;

use std::collections::HashMap;
use std::rc::Rc;

fn main() {
    let mut some_map = HashMap::new();
    some_map.insert("a", Rc::new(1));

    let val_rc = Rc::clone(some_map.get("a").unwrap());
    if *val_rc != 2 {
        some_map.insert("b", val_rc);
    }

}

以前的答案版本

很难说出您到底在寻找什么,但在这种特殊情况下,如果您只需要检查值,则在更新哈希图之前销毁借用的值。肮脏丑陋的代码应该是这样的:

fn main() {
    let mut some_map = HashMap::new();
    some_map.insert("a", 1);

    let is_ok = false;

    {
        let some_val = some_map.get("a").unwrap();
        is_ok = *some_val != 2;
    }

    if is_ok {
        some_map.insert("b", *some_val);
    }
}

【讨论】:

  • ...我怎么没想到呢?伙计,我很笨。假设我需要在循环的每次迭代中重用该值。我是否必须在每次迭代中获取并销毁该值,还是有办法在整个循环中保留该值?
  • @VeryCasual 你得到的只是一个对象的引用,所以制作和销毁它并不昂贵,除了在哈希图中搜索键的时间。在任何语言中,在更改集合时使用主动引用都是一种不好的做法。哈希映射对键搜索的摊销复杂度为O(1),所以除非你的循环超级热并且它总是每秒执行一百万次迭代(比如图形渲染器或网络服务器),你不需要担心破坏该引用并再次搜索它,性能影响还不错。
  • 啊,当然。我也应该想到这一点。 Rust 似乎在煎熬我的大脑,让我忘记简单的事情。将此答案标记为已接受。
  • 我不明白。 some_val 不会超出最后一行的范围吗?
  • @SvenMarnach 确实如此。然后可能需要引用计数,如果可以,请 OP 不接受答案:) 我没有看到您尝试复制该值而不克隆它
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-22
  • 2012-11-23
  • 2020-04-09
  • 2017-07-06
相关资源
最近更新 更多