[SOLVED] Cannot infer an appropriate lifetime for autoref due to conflicting requirements


#1

I’m trying to store a list of strings under a string key, but seem to be unable to because of this error.

Here’s a simpler version of the code causing it:

use std::collections::HashMap;
use std::vec::Vec;

struct Test<'a> {
    tests: HashMap<String, &'a mut Vec<String>>,
}

impl<'a> Test<'a> {
    pub fn add(&mut self, a: &str, b: &[String]) {
        for c in b {
            let string_a = String::from(a.to_owned());
            match self.tests.get(&string_a) {
                Some(items) => {
                    items.push(String::from(c.to_owned()));
                    self.tests.insert(string_a, &mut items);
                },
                _ => {
                    let items = Vec::new();
                    items.push(String::from(a.to_owned()));
                    self.tests.insert(string_a, &mut items);
                }
            }
        }
    }
}

fn main() {
    let test = Test {
        tests: HashMap::new(),
    };
    test.add(&"a", &[String::from("b")]);
}

#2

There are several issues with your code but the biggest is your None case - you cannot add a reference to a temp Vec into the HashMap because the HashMap is storing references to Vecs coming from outside the Test type.

Is there a reason you don’t want to do HashMap<String, Vec<String>>?


#3

I’m not exactly sure what I should be storing here, super new to Rust. Was just making a guess at what should be stored there. Wouldn’t the vector need to be mutable, so I can add / remove items from it in the future?


#4

No worries about being new - you’ve come to the right place.

If the HashMap owns the Vec, as in what I suggested above, then in turn your Test type owns it. If you have mutable access to Test (which you do since you’re borrowing it &mut) you can also mutate the vec. It’s a package deal.

I would recommend reading the Rust book if you’ve not done that yet. It’ll cover things like this.


#5

I have switched the HashMap’s types, and nearly gotten this to a point where it’s compiling.

error[E0596]: cannot borrow immutable borrowed content `*items` as mutable
  --> src/main.rs:37:17
   |
37 |                 items.push(String::from(c.to_owned()));
   |                 ^^^^^ cannot borrow as mutable

error[E0502]: cannot borrow `self.tests` as mutable because it is also borrowed as immutable
  --> src/main.rs:38:17
   |
36 |             if let Some(items) = self.tests.get(&string_a) {
   |                                  ---------- immutable borrow occurs here
37 |                 items.push(String::from(c.to_owned()));
38 |                 self.tests.insert(string_a, items.to_vec());
   |                 ^^^^^^^^^^ mutable borrow occurs here
39 |             }
   |             - immutable borrow ends here

Relevant code:

            if let Some(items) = self.tests.get(&string_a) {
                items.push(String::from(c.to_owned()));
                self.tests.insert(string_a, items.to_vec());
            }

I have read majority of the rust documentation, and to my understanding that’s essentially the book.


#6

Here’s a way to do it: http://play.integer32.com/?gist=bd31a3fa8ba3f16d6b4a117f1f90ca48

Note that, unfortunately, doing the “get or insert into HashMap” in the most straightforward way bumps into a limitation of the current borrow checker. Here’s a blog that provides details: http://smallcultfollowing.com/babysteps/blog/2016/04/27/non-lexical-lifetimes-introduction/.

The entry() API allows you to sidestep this problem and, as a bonus, makes the code more concise.


#7

Oh, I hadn’t remembered that existed! Thank you so much for showing me the correct way!