Creation of a hashmap with struct in Rust

10,679

This simplified example seems to work:

use std::collections::HashMap;

#[derive(Clone)] // we'll be cloning it later on
struct Node<'a> {
    data: &'a i32 
}


struct Test<'a> {
    hash_map: HashMap<&'a str, Node<'a>>  // the hash map owns the struct
}

impl<'a> Test<'a> {
    fn new() -> Test<'a> {
        Test {hash_map: HashMap::new()}
    }

    fn join(
        &mut self, // must be mutable
        node: Node<'a>) { // do not pass a reference
        self.hash_map.insert("test", node);  // inserting moves `node`
    }
}

fn main() {
    let stuff = Node {data: &12};
    let mut test = Test::new();

    test.join(stuff.clone());  // if we don't clone, `stuff` will get moved

    println!("{}", *test.hash_map["test"].data);  // outputs "12"
}

Since std::collections::HashMap::insert attempts to move its second argument, one can't dereference a pointer to something and pass that to this method because otherwise the pointer will become uninitialized, which isn't permitted. A way so solve this is to pass a moved value and not a pointer to join.

Share:
10,679
carlos.baez
Author by

carlos.baez

Updated on July 10, 2022

Comments

  • carlos.baez
    carlos.baez almost 2 years

    I am trying to set up a hashmap of objects / structs in rust... But I don't understand this concrete problem (a lifetime error).

    #[derive(Hash, Eq, PartialEq)]
    #[derive(Serialize, Deserialize, Debug)]
    pub struct Node<'a> {
        identifier: &'a str,
        sha_id: Vec<u8>,
        successor_id: Option<Vec<u8>>,
        predecessor_id: Option<Vec<u8>>,
    }
    
    
    impl<'a> Node<'a> {
        ...
        ..
        .
    }
    
    pub struct Application<'a> {
        hash_map: HashMap<&'a str, Node>,
    }
    
    impl<'a> Application<'a> {
        fn join(&self, node: &Node) {
            self.hash_map.insert(node.identifier, node);
        }
    }
    

    The error is a missing lifetime specifier in the hash_map: HashMap<&'a str, Node> that I tried to solve changing Node to Node<'a> but It throws a "mismatched type" error when I try to insert...

    I don't exactly why I have this problem missing the lifetime and I don't find solutions..

    UPDATE:

    #[derive(Hash, Eq, PartialEq)]
    #[derive(Serialize, Deserialize, Debug)]
    pub struct Node<'a> {
        identifier: &'a str,
        sha_id: Vec<u8>,
        successor_id: Option<Vec<u8>>,
        predecessor_id: Option<Vec<u8>>,
    }
    
    
    impl<'a> Node<'a> {
        ...
        ..
        .
    }
    
    pub struct Application<'a> {
        hash_map: HashMap<&'a str, Node<'a>>,
    }
    
    impl<'a> Application<'a> {
        fn join(&self, node: &Node) {
            self.hash_map.insert(node.identifier, *node);
        }
    }
    

    And the output is:

    "explicit lifetime required in the type of `node`"
    

    UPDATE2:

    pub struct Application<'a> {
        hash_map: HashMap<&'a str, Node<'a>>,
    }
    
    impl<'a> Application<'a> {
        fn join(&mut self, node: &'a Node<'a>) {
            self.hash_map.insert(node.identifier, *node);
        }
    
    }
    

    And the output is:

    self.hash_map.insert(node.identifier, *node); cannot move out of borrowed content
    

    COMPLETE SOLUTION

    #[derive(Clone, Hash, Eq, PartialEq)]
    #[derive(Serialize, Deserialize, Debug)]
    pub struct Node<'a> {
        identifier: &'a str,
        sha_id: Vec<u8>,
        successor_id: Option<Vec<u8>>,
        predecessor_id: Option<Vec<u8>>,
    }
    
    
    impl<'a> Node<'a> {
    ...
    ..
    .
    }
    
    pub struct Application<'a> {
        hash_map: HashMap<&'a str, Node<'a>>,
    }
    
    impl<'a> Application<'a> {
        fn join(&mut self, node: Node<'a>) {
            self.hash_map.insert(node.identifier, node);
        }
    
    }
    
    • Boiethios
      Boiethios over 5 years
      insert takes first the key and then the value...
    • carlos.baez
      carlos.baez over 5 years
      sorry, I didn't paste correctly the code.. I edit the post, but the error is the same...
    • ForceBru
      ForceBru over 5 years
      As far as I understand, HashMap<&'a str, Node> should provide a lifetime specifier to Node because it requires one by definition. So, it should be HashMap<&'a str, Node<'a>>.
    • carlos.baez
      carlos.baez over 5 years
      Yes, but when I do this, I get a mismatched type error... I don't exactly why...
    • Boiethios
      Boiethios over 5 years
      You have 3 or 4 errors, but if you follow the compiler's advices, you should address all of them.
    • carlos.baez
      carlos.baez over 5 years
      I understand that It can be tedious, but in fact, I have only one error, but when I fix one, It throws another one...
    • Joe Clay
      Joe Clay over 5 years
      It should be telling you which line requires an explicit lifetime in the error message - my hunch is that the &Node on join needs to be &Node<'a>, or possibly even &'a Node<'a>.
    • ForceBru
      ForceBru over 5 years
      @JoeClay, if you do this, though, Rust will refuse to move out of borrowed content with the last version of the code because, apparently, insert attempts to move the second argument.
    • Joe Clay
      Joe Clay over 5 years
      Then they need to clone the data, or have the hashmap store references. A HashMap always owns the data stored inside of it.
    • carlos.baez
      carlos.baez over 5 years
      I can understand this. Directly, it is a bad design... I can try to refactor all the code to clone data if you think it is the problem...
  • hellow
    hellow over 5 years
    While your code snippet might answer the question, it would be good if you could add a few sentences what was wrong and how you solved it, at best with some links to documentation or similar problems.