首页 > 解决方案 > 折叠字符串以在 rust 中构建 hashmap 字符计数器,但给出两阶段借用错误

问题描述

我正在尝试建立一个计算字符串中字符频率的哈希图。我的方法是折叠字符串中的字符,在每次迭代时增加当前字符的计数。不幸的是,rust 告诉我,我在借用方面做错了。

使用clone()或将其拆分hm.get(&c)到单独的行没有任何影响。

我看不出如何避免这个关于两阶段借用的错误。(相关的 rust-lang github 问题

use std::collections::HashMap;

fn main() {
    let some_str = "some string";

    let hm = some_str.chars().fold(HashMap::new(), |mut hm, c| {
        match hm.get(&c) {
            None => hm.insert(c, 1),
            Some(i) => hm.insert(c, i + 1),
        };
        hm
    });

    for (key, val) in hm.iter() {
        println!("{}: {}", key, val);
    }
}

这给出了这个错误

warning: cannot borrow `hm` as mutable because it is also borrowed as immutable
 --> dank.rs:9:24
  |
7 |         match hm.get(&c) {
  |               -- immutable borrow occurs here
8 |             None => hm.insert(c, 1),
9 |             Some(i) => hm.insert(c, i+1)
  |                        ^^           - 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>

标签: rusthashmapfoldborrow-checker

解决方案


感谢@Stargateur 回答我的问题

问题是,当我们i + 1重新插入 hashmap 时,变量i实际上是从 hashmap 借来的。如果我们首先复制 i,我们对复制的不可变借用hm结束于我们复制的位置i,这是在我们使用对 的可变引用hm之前insert()

use std::collections::HashMap;

fn main() {
    let some_str = "some string";

    let hm = some_str.chars().fold(HashMap::new(), |mut hm, c| {
        match hm.get(&c) { // borrow of hm
            None => hm.insert(c, 1),
            Some(i) => { // i is a reference
                let j = *i; // i is Copy so we copy it, j is not a reference owned by hm, so hm is not borrowed anymore
                hm.insert(c, j + 1) // we can borrow hm mutable
            }
        };
        hm
    });

    for (key, val) in hm.iter() {
        println!("{}: {}", key, val);
    }
}

此外,一个更好的解决方案是使用Entry api通过以下代码完全解决这个问题:

use std::collections::HashMap;

fn main() {
    let some_str = "some string";

    let hm = some_str.chars().fold(HashMap::new(), |mut hm, c| {
        *hm.entry(c).or_insert(0) += 1;
        hm
    });

    for (key, val) in hm.iter() {
        println!("{}: {}", key, val);
    }
}

推荐阅读