Jake Ireland
Jake Ireland

Reputation: 652

Borrowing and temporary variables in hashmap is throwing circular errors

I am having trouble getting a piece of code to work, as the errors I am getting are circular.

I start with the following code:

use std::collections::HashMap;

let mut hashm = HashMap::<Vec<&str>, &str>::new();
for i in v {
    let s = i.split('\t').collect::<Vec<&str>>();
    let j = s[0]
        .replace(&['{', '}'][..], "") // replace multiple at once; https://users.rust-lang.org/t/24554/2
        .split(", ")
        .collect::<Vec<&str>>();
    let k = s[1];
    hashm.insert(j, k);
}

The error I get says that s[0]

creates a temporary which is freed while still in use... 
hashm.insert(j, k);
             - borrow later used here

So I change hashm.insert(j, k) to hashm.insert(&j, k) and HashMap::<Vec<&str>, &str>::new(); to HashMap::<&Vec<&str>, &str>::new();, but then I get the same error, as well as this:

hashm.insert(&j, k);
             ^^ borrowed value does not live long enough

How can I rectify this?

Upvotes: 2

Views: 140

Answers (1)

Kevin Reid
Kevin Reid

Reputation: 43902

let mut hashm = HashMap::<Vec<&str>, &str>::new();

The key and value types you have specified here are borrowed. This means that something else must own the keys and values. That can work in some cases, but in this case you're calling str::replace which returns a newly allocated String which is then borrowed by split. That string cannot outlive the loop iteration — in fact, it's freed at the end of the let j statement. If you assigned the String to its own temporary variable, it'd last long enough that the call to split would compile, but it would still be freed at the end of the loop, so inserting it in hashm would fail.

If you make Strings from the &strs for the keys then the code will compile:

use std::collections::HashMap;

let mut hashm = HashMap::<Vec<String>, &str>::new();
for i in v {
    let s = i.split('\t').collect::<Vec<&str>>();
    let j = s[0]
        .replace(&['{', '}'][..], "")
        .split(", ")
        .map(str::to_owned)
        .collect();
    let k = s[1];
    hashm.insert(j, k);
}

Note that this HashMap still cannot live any longer than v because its values are still borrowed. If that is not what you want, then you must change its type to HashMap<Vec<String>, String> and use hashm.insert(j, k.to_owned());.

Upvotes: 2

Related Questions