сверните строку, чтобы построить счетчик символов hashmap в ржавчине, но выдает ошибку двухфазного заимствования - PullRequest
0 голосов
/ 12 марта 2020

Я пытаюсь создать хэш-карту, которая считает символьные частоты в строке. Мой подход заключается в сворачивании символов в строке, увеличивая счетчик текущего символа на каждой итерации. К сожалению, ржавчина говорит мне, что я делаю что-то не так с заимствованиями.

Никаких изменений в использовании 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>

1 Ответ

2 голосов
/ 13 марта 2020

кредит @Stargateur за ответ на мой вопрос

Проблема заключалась в том, что когда мы вставляем i + 1 обратно в хэш-карту, переменная i фактически заимствуется из хэш-карты . Если мы сначала скопируем 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);
    }
}
...