Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

to_string() causes error "borrowed value does not live long enough"

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.

like image 845
Caballero Avatar asked Sep 13 '25 16:09

Caballero


1 Answers

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.

like image 155
Lukas Kalbertodt Avatar answered Sep 17 '25 20:09

Lukas Kalbertodt