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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With