Search code examples
rusthashmapownership

Accessing a nested HashMap in Rust with None


I wanted to create a data structure similar to the one found in this post. So, a tree of Database nodes that contain some data associated with that node, as well as a node at a further depth.

The difference is that I want to allow the possibility of children being None, to indicate that the node is a leaf.

So it should look like:

{
  "1": Database {
    {
      data: "element 1",
      children: Some({
        "a": Database {
          data: "element 1-a",
          children: None
        },
        "b": Database {
          data: "element 1-b",
          children: None
        }
      })
    }
  },
  "2": Database {
    {
      data: "element 2",
      children: None
    }
  }
}

Using the code in the original post, I've come up with this [playground link]:

#[derive(Default, Debug)]
struct Database {
    children: Option<HashMap<String, Database>>,
    data: String,
}

impl Database {
    fn insert_path(&mut self, path: &[&str]) -> &mut Self {
        let mut node = self;
        for &subkey in path.iter() {
            if let None = node.children {
                node.children = Some(HashMap::new());
            }
            node = node
                .children
                .unwrap()
                .entry(subkey.to_string())
                .or_insert_with(Database::default);
        }
        node
    }
}

fn main() {
    let mut db = Database {
        children: Some(HashMap::new()),
        data: "root".to_string(),
    };

    let node = db.insert_path(&vec!["key1", "key1.1", "key1.1.3"]);
    node.data = "myvalue".to_string();

    println!("{:#?}", db);
}

This doesn't work. I get the following errors:

error[E0507]: cannot move out of `node.children` which is behind a mutable reference
  --> src/main.rs:18:20
   |
18 |               node = node
   |  ____________________^
19 | |                 .children
   | |_________________________^ move occurs because `node.children` has type `Option<HashMap<String, Database>>`, which does not implement the `Copy` trait
   |
help: consider borrowing the `Option`'s content
   |
18 |             node = node
19 |                 .children.as_ref()
   |

error[E0515]: cannot return value referencing temporary value
  --> src/main.rs:24:9
   |
18 |               node = node
   |  ____________________-
19 | |                 .children
20 | |                 .unwrap()
   | |_________________________- temporary value created here
...
24 |           node
   |           ^^^^ returns a value referencing data owned by the current function

I'm very confused as to why this is happening. I think that using unwrap() on node.children drops the moved value node.children. However, I can't see how to do this without using unwrap(). How might I be able to achieve the functionality from the original post with this new structure that uses None? Is this even possible?

Note: I've also chopped down the original, so that it is more similar to the above code and easier to compare. See here for a playground link.


Solution

  • Your example compiles if you add an as_mut() after children, i.e.:

    node = node
        .children
        .as_mut()
        .unwrap()
        .entry(subkey.to_string())
        .or_insert_with(Database::default);
    

    Option::as_mut turns an Option<T> into an Option<&mut T>, thus preventing the move out of node when you unwrap() node.children.