3

I want to implement a tree data structure. I have a Node struct and want it to hold references to child Nodes. I tried:

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());
}

This code does not compile:

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

How can I implement this?

Shepmaster
440k116 gold badges1.3k silver badges1.5k bronze badges
asked Apr 2, 2017 at 15:31
2
  • 1
    Likely duplicate of Is there any way to return a reference to a variable created in a function? Commented Apr 2, 2017 at 16:00
  • 1
    @Shepmaster Probably not. There are multiple issues, but OP wants to insert the newly created value into a hashmap and return a reference to the value inside of the hashmap (see the second error which is more important in this case). Commented Apr 2, 2017 at 18:18

1 Answer 1

3

In this case, it's actually necessary to look at the second error message in the compiler output:

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

The variable node is moved into the hashmap in line 21. You can't use it afterwards! In Rust we have move semantics, meaning that everything gets moved by default instead of cloned by default (C++) or referenced by default (Java). You want to return a reference to the Node object inside the hashmap!

An easy way would be to insert node as you are already doing and afterwards fetching the value from the hashmap:

let mut node = Node::new(value);
self.children.insert(key.clone(), node);
self.children.get_mut(key).unwrap()

This should make clear what the function actually does. However, this code has some disadvantages: First, we have to clone key (we need it for the insertion and the query) and secondly, the hashmap needs calculate the hash of the key twice which is not very efficient.

Luckily, Rust's HashMap has a nice entry()-API. We could change the function like that:

self.children.entry(key).or_insert_with(|| Node::new(value))

This is the whole body of add_child()! Now, however, we notice that ... we haven't really thought about what is supposed to happen if the hashmap already contains a value associated with the given key! In the code above, the old value is kept and returned. If you want to do something else (e.g. replace the value), you could just use match on the Entry object:

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),
}
answered Apr 2, 2017 at 18:32
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.