Как я могу одновременно перебрать Rust HashMap и изменить некоторые его значения?

В этом году я пробую Advent of Code в Rust как способ изучения языка. Я проанализировал входные данные (начиная с 7-го дня) в следующую структуру:

struct Process {
    name: String,
    weight: u32,
    children: Vec<String>,
    parent: Option<String>
}

Они хранятся в HashMap<String, Process>. Теперь я хочу перебрать значения на карте и обновить родительские значения, основываясь на том, что я нахожу в родительском векторе «дети».

Что не работает, так это

for p in self.processes.values() {
    for child_name in p.children {
        let mut child = self.processes.get_mut(child_name).expect("Child not found.");
        child.parent = p.name;
    }
}

У меня не может быть и изменяемой ссылки на HashMap (self.processes) и не изменяемая ссылка или две изменяемые ссылки.

Итак, какой самый идиоматичный способ сделать это в Rust? Я вижу два варианта:

  1. Скопируйте отношения родитель /потомок в новую временную структуру данных за один проход, а затем обновите структуры процесса на втором проходе после того, как неизменяемая ссылка выйдет из области видимости.
  2. Измените мою структуру данных, чтобы поместить "parent" в собственный HashMap.

Есть ли третий вариант?

4 голоса | спросил Mark Gritter 10 SunEurope/Moscow2017-12-10T11:20:18+03:00Europe/Moscow12bEurope/MoscowSun, 10 Dec 2017 11:20:18 +0300 2017, 11:20:18

1 ответ


0

Да, вы можете предоставить внутреннюю изменчивость значениям HashMap, используя RefCell :

struct ProcessTree {
    processes: HashMap<String, RefCell<Process>>,  // change #1
}

impl ProcessTree {
    fn update_parents(&self) {
        for p in self.processes.values() {
            let p = p.borrow();                    // change #2
            for child_name in &p.children {
                let mut child = self.processes
                    .get(child_name)               // change #3
                    .expect("Child not found.")
                    .borrow_mut();                 // change #4
                child.parent = Some(p.name.clone());
            }
        }
    }
}

borrow_mut будет паниковать во время выполнения, если ребенок уже занят с borrow. Это происходит, если процесс является собственным родителем (что, по-видимому, никогда не должно происходить, но в более надежной программе вы захотите выдать значимое сообщение об ошибке, а не просто паниковать).

Я придумал несколько имен и внес несколько небольших изменений (помимо специально указанных), чтобы этот код компилировался. В частности, p.name.clone() создает полную копию p.name. Это необходимо, потому что name и parent принадлежат String s.

ответил trentcl 10 SunEurope/Moscow2017-12-10T15:15:40+03:00Europe/Moscow12bEurope/MoscowSun, 10 Dec 2017 15:15:40 +0300 2017, 15:15:40

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132