aRtoo
aRtoo

Reputation: 1892

Rust Lifetime on HashMap

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:

enter image description here

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

Answers (1)

EvilTak
EvilTak

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

Related Questions