Реализовать древовидную структуру данных
Я хочу реализовать древовидную структуру данных. у меня есть Node
структура и хотите, чтобы он содержал ссылки на ребенка Node
s. Я старался:
use std::collections::*;
#[derive(Debug)]
struct Node {
value: String,
children: HashMap<String, Node>,
}
impl Node {
fn new(value: String) -> Self {
Node {
value: value,
children: HashMap::new(),
}
}
fn add_child(&mut self, key: String, value: String) -> &mut Node {
let mut node = Node::new(value);
self.children.insert(key, node);
&mut node
}
}
fn main() {
let mut root_node = Node::new("root".to_string());
root_node.add_child("child_1_1".to_string(), "child_1_1_value".to_string());
}
Этот код не компилируется:
error: `node` does not live long enough
--> src/main.rs:22:10
|
22 | &mut node
| ^^^^ does not live long enough
23 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 19:67...
--> src/main.rs:19:68
|
19 | fn add_child(&mut self, key: String, value: String) -> &mut Node {
| ____________________________________________________________________^ starting here...
20 | | let mut node = Node::new(value);
21 | | self.children.insert(key, node);
22 | | &mut node
23 | | }
| |___^ ...ending here
error[E0382]: use of moved value: `node`
--> src/main.rs:22:10
|
21 | self.children.insert(key, node);
| ---- value moved here
22 | &mut node
| ^^^^ value used here after move
|
= note: move occurs because `node` has type `Node`, which does not implement the `Copy` trait
Как я могу это реализовать?
1 ответ
В этом случае на самом деле необходимо посмотреть второе сообщение об ошибке в выводе компилятора:
error[E0382]: use of moved value: `node`
--> src/main.rs:22:10
|
21 | self.children.insert(key, node);
| ---- value moved here
22 | &mut node
| ^^^^ value used here after move
|
= note: move occurs because `node` has type `Node`, which does not implement the `Copy` trait
Переменная node
перемещен в хэш-карту в строке 21. Впоследствии вы не сможете использовать его! В Rust у нас есть семантика перемещения, что означает, что все перемещается по умолчанию, а не клонируется по умолчанию (C++) или на него ссылаются по умолчанию (Java). Вы хотите вернуть ссылку на Node
объект внутри hashmap!
Простой способ будет вставить node
как вы уже делаете, а потом извлекаете значение из hashmap:
let mut node = Node::new(value);
self.children.insert(key.clone(), node);
self.children.get_mut(key).unwrap()
Это должно прояснить, что на самом деле делает функция. Однако этот код имеет некоторые недостатки: во-первых, мы должны клонировать key
(нам это нужно для вставки и запроса), а во-вторых, хэш-карте необходимо дважды вычислить хэш ключа, что не очень эффективно.
К счастью, Руст HashMap
имеет хороший entry()
-АПИ. Мы могли бы изменить функцию следующим образом:
self.children.entry(key).or_insert_with(|| Node::new(value))
Это все тело add_child()
! Теперь, однако, мы замечаем, что... мы действительно не думали о том, что должно произойти, если хэш-карта уже содержит значение, связанное с данным ключом! В приведенном выше коде старое значение сохраняется и возвращается. Если вы хотите сделать что-то еще (например, заменить значение), вы можете просто использовать match
на Entry
объект:
let node = Node::new(value);
match self.children.entry(key) {
Entry::Occupied(e) => {
// Maybe you want to panic!() here... but we will just
// replace the value:
e.insert(node); // discarding old value...
e.get_mut()
}
Entry::Vacant(e) => insert(node),
}