s.

м году я пробую 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? Я вижу два варианта:

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

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

 Stefan10 дек. 2017 г., 10:23
Я бы наверное пошел наVec<Process>и использоватьusize как ссылка на другие записи. Можно использовать двухпроходный подход: сначала собратьHashMap<String, usize> и заполнитьname + weightво втором проходе заполнитеchildren а такжеparent, Много возможностей :)
 Sven Marnach10 дек. 2017 г., 11:29
Другим вариантом будет изменить типparent вRefCell<Option<String>> - это позволит изменить родителя, даже если у вас есть только константная ссылка.
 Sven Marnach10 дек. 2017 г., 11:31
Также обратите внимание, что вам нужно клонировать строку, чтобы скопировать ее изp.name вchild.parent - вы не можете вывести его из заимствованного контекста (и вы бы этого не хотели, даже если бы могли).
 Stefan10 дек. 2017 г., 10:13
Мне кажется очевидным, чтоself.processes этоHashMap<String, Process>.
 ljedrz10 дек. 2017 г., 10:57
@Stefan: мне просто не нравится работать с неполным кодом; Я думаю, что это хороший вопрос, чтобы рассуждать, хотя.

Ответы на вопрос(1)

Решение Вопроса

вы можете предоставить внутреннюю изменчивость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 принадлежатStrings.

Ваш ответ на вопрос