4

Why is to_string() causing borrowed value does not live long enough error? Example below:

use std::collections::HashMap;

struct Foo {
    id: Option<usize>,
    name: String
}

fn main() {

    let foos = getFoos();

    for foo in foos {
        let mut map = HashMap::new();
        map.insert("name", &foo.name);
        map.insert("id", &foo.id.unwrap().to_string());
    }

}

fn getFoos() -> Vec<Foo> {
    Vec::new()
}

Error:

src/main.rs:15:27: 15:54 error: borrowed value does not live long enough
src/main.rs:15         map.insert("id", &foo.id.unwrap().to_string());
                                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:13:38: 16:6 note: reference must be valid for the block suffix following statement 0 at 13:37...
src/main.rs:13         let mut map = HashMap::new();
src/main.rs:14         map.insert("name", &foo.name);
src/main.rs:15         map.insert("id", &foo.id.unwrap().to_string());
src/main.rs:16     }
src/main.rs:15:9: 15:56 note: ...but borrowed value is only valid for the statement at 15:8
src/main.rs:15         map.insert("id", &foo.id.unwrap().to_string());
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:15:9: 15:56 help: consider using a `let` binding to increase its lifetime
src/main.rs:15         map.insert("id", &foo.id.unwrap().to_string());
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Why is compiler suggesting to create intermediate value? This error is confusing.

Caballero
  • 11,546
  • 22
  • 103
  • 163
  • You are taking a reference to the value generated by `to_string`. Just remove the `&` and your code will work: https://play.rust-lang.org/?gist=b41ea549d5b4add70559827b7d41e58a&version=stable&backtrace=0 – oli_obk May 23 '16 at 09:42
  • Possible duplicate of [Return local String as a slice (&str)](http://stackoverflow.com/questions/29428227/return-local-string-as-a-slice-str) – oli_obk May 23 '16 at 09:44
  • 1
    @ker I don't think it's a duplicate. Similar yes, but still different enough IMO :) – Lukas Kalbertodt May 23 '16 at 09:46
  • You probably don't want to insert references to strings into the map. Just remove all the & operators. – Sebastian Redl May 23 '16 at 09:48

1 Answers1

6

You are creating a HashMap that saves references to strings, namely &String. If we would annotate the type, it would look like this:

let mut map: HashMap<&str, &String> = HashMap::new();

This means that the map contains a lot of references to objects that live somewhere else. In your first insert, that works perfectly fine, since foo.name lives somewhere else, specifically in the object foo.

map.insert("name", &foo.name);

But your second insert has a problem: you want to reference a String object, that lives somewhere. to_string() creates a String which is returned by the function, but in your case it's just a temporary object. The object will be destroyed after the line is executed.

map.insert("id", &foo.id.unwrap().to_string());

The compiler is right: a let binding would solve the problem here.

let mut map = HashMap::new();
map.insert("name", &foo.name);
let id_string = foo.id.unwrap().to_string();
map.insert("id", &id_string);

This works fine in your small example, but it could be more complicated when you are working on something bigger. For example, if the HashMap would be defined outside of the loop, you would have a problem because the reference you insert into the map needs to live at least as long as the map itself.

Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305