carlos.baez
carlos.baez

Reputation: 1143

Creation of a hashmap with struct in Rust

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

}

Upvotes: 6

Views: 24048

Answers (2)

Matthew Stewart
Matthew Stewart

Reputation: 71

For poor idiots like myself, who are trying to find out how to put hashmaps in a struct, no need to spend many hours "playing" with lifetimes(the 'a in the above example). They are not required in the slightest, just use String instead of &str in your structure.

Edit: @neo made a slightly clearer version of my below example at rust-lang

struct ComputerConfig {
    hostname: String, 
    // displays: Vec<DispConfig>,
}


struct MyConfig {
    pub config_version: u8,
    computers: HashMap<String, ComputerConfig>, // the hash map owns the struct
}

impl MyConfig {
    fn new() -> MyConfig {
        MyConfig {
            computers: HashMap::new(),
            config_version: 1,
        }
    }

    /// Join is used to add a new ComputerConfig into the hashmap
    fn join(
        &mut self, // must be mutable
        key: &str,
        node: ComputerConfig,
    ) {
        // do not pass a reference
        self.computers.insert(key.to_string(), node); // inserting moves `node`
    }
}

fn main() {

  let mut cfg = MyConfig::new()

  cfg.join("test", stuff); 

  println!("{:?}", &cfg); // outputs "12"

}

Upvotes: 4

ForceBru
ForceBru

Reputation: 44830

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.

Upvotes: 7

Related Questions