Reputation: 1892
How do you add lifetime on the HashMap when inserting a key?
I have this setup on my struct and have been getting this error:
// ! ERROR: a value of type `std::collections::HashMap<std::string::String, u64>` cannot be built from an iterator over elements of type `(&str, {integer})`
so I did this. I added a lifetime to my struct.
#[derive(Debug)]
pub struct LInstruction<'a> {
pub label: HashMap<&'a str, u64>
}
impl<'a> LInstruction<'a> {
pub fn new() -> LInstruction<'a> {
LInstruction {
label: [
("R0", 0),
("R1", 1),
("R2", 2),
("R3", 3),
("R4", 4),
("R5", 5),
("R6", 6),
("R7", 7),
("R8", 8),
("R9", 9),
("R10", 10),
("R11", 11),
("R23", 12),
("R13", 13),
("R14", 14),
("R15", 15),
("SP", 0),
("LCL", 1),
("ARG", 2),
("THIS", 3),
("THAT", 4),
("SCREEN", 16384),
("KBD", 24576)
].iter().cloned().collect()
}
}
}
but then, when I created this method for my struct.
pub fn add_labeler(&self, tokens: Vec<&str>) {
let mut counter = 0;
for token in tokens {
if token.starts_with("(") && token.ends_with(")") {
let label_name = token.trim_start_matches("(").trim_end_matches(")");
&self.label.insert(label_name, counter);
}
counter += 1;
}
}
I got this error:
the label_name
is having some lifetime issue. I don't know where to put the lifetime in the label_name variable because trim_start and trim_end
returns a &str
(string slice).
So my current solution, for now, is to remove the lifetime, Change the HashMap key type to String
then do this to the initial value, but then it'll be cumbersome to add the .to_string()
method to each string slice.
[
("R0".to_string(), 0),
("R1".to_string(), 1),
("R2".to_string(), 2),
("R3".to_string(), 3),
...
]
This is my 3rd day learning Rust so I'm not familiar with lifetimes.
Upvotes: 0
Views: 2444
Reputation: 7579
In the declaration of LInstruction
, you specify a lifetime 'a
of the &str
references stored inside the label
HashMap
field. In the struct's impl
, you call this lifetime 'a
(using the syntax impl<'a> LInstruction<'a>
). To put it simply, this implies that each &str
reference stored in the label
HashMap
field of every self
instance has a lifetime of 'a
. In other words, when you try and insert a &str
key into self.label
(as you do in add_labeler
), the reference you add must also have a lifetime of 'a
.
In add_labeler
, you are attempting to add the label_name
to self.label
as a key. Since label_name = token.trim_start_matches("(").trim_end_matches(")")
, label_name
is a slice of the same String
that token
is a slice of. Because both slices point to data from the same source, they have the same lifetime. As token
is an element of tokens
, its lifetime is specified in the tokens
parameter signature.
Due to lifetime elision, the lifetime of the &str
references in token
is different than the lifetime of &self
. The unelided signature would be fn add_labeler<'b>(&self, tokens: Vec<&'b str>)
-- recall that the &str
references in self.label
(and by extension &self
) have a lifetime 'a
as defined in the impl
. This is now a problem, as token
would have a lifetime 'b
, which does not satisfy the lifetime requirement 'a
needed for it to be safely added to self.label
. The solution to this is to specify that the references in tokens
must indeed have the same lifetime as the references stored in self.label
, by explicitly adding the 'a
lifetime to its type (tokens: Vec<&'a str>
).
The compiler error tells you exactly where to put the lifetime as well:
error[E0621]: explicit lifetime required in the type of `tokens`
--> src/lib.rs:46:35
|
41 | pub fn add_labeler(&self, tokens: Vec<&str>) {
| --------- help: add explicit lifetime `'a` to the type of `tokens`: `Vec<&'a str>`
...
46 | self.label.insert(label_name, counter);
| ^^^^^^^^^^ lifetime `'a` required
Note that in order to get add_labeler
to compile, you will also have to borrow self
as mutable with &mut self
as you are modifying the self.label
field in the function body.
Upvotes: 2