Creation of a hashmap with struct in Rust
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
.
carlos.baez
Updated on July 10, 2022Comments
-
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 over 5 years
insert
takes first the key and then the value... -
carlos.baez over 5 yearssorry, I didn't paste correctly the code.. I edit the post, but the error is the same...
-
ForceBru over 5 yearsAs far as I understand,
HashMap<&'a str, Node>
should provide a lifetime specifier toNode
because it requires one by definition. So, it should beHashMap<&'a str, Node<'a>>
. -
carlos.baez over 5 yearsYes, but when I do this, I get a mismatched type error... I don't exactly why...
-
Boiethios over 5 yearsYou have 3 or 4 errors, but if you follow the compiler's advices, you should address all of them.
-
carlos.baez over 5 yearsI 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 over 5 yearsIt should be telling you which line requires an explicit lifetime in the error message - my hunch is that the
&Node
onjoin
needs to be&Node<'a>
, or possibly even&'a Node<'a>
. -
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 over 5 yearsThen they need to clone the data, or have the hashmap store references. A
HashMap
always owns the data stored inside of it. -
carlos.baez over 5 yearsI 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 over 5 yearsWhile 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.