Reputation: 7124
tl;dr error[E0716]: temporary value dropped while borrowed
is a difficult and common problem, is there a consistent solution?
I've run into the difficult rustc error
error[E0716]: temporary value dropped while borrowed
...
creates a temporary which is freed while still in use
Searching Stackoverflow, there are many questions for this rust error error[E0716]
.
Maybe a rust expert can provide a general solution for this common newbie problem, a solution good enough that it might also Answer the linked Questions (see below).
A concise code sample to demonstrate the problem (rust playground):
type Vec1<'a> = Vec::<&'a String>;
fn fun1(s1: &String, v1: &mut Vec1) {
v1.insert(0, &s1.clone());
}
fn main() {
let mut vec1 = Vec::new();
let str1 = String::new();
fun1(&str1, &mut vec1);
}
result:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:4:19
|
3 | fn fun1(s1: &String, v1: &mut Vec1) {
| -- has type `&mut Vec<&'1 String>`
4 | v1.insert(0, &s1.clone());
| --------------^^^^^^^^^^-- temporary value is freed at the end of this statement
| | |
| | creates a temporary which is freed while still in use
| argument requires that borrow lasts for `'1`
For more information about this error, try `rustc --explain E0716`.
My understanding is given the statement v1.insert(0, &s1.clone());
,
the s1.clone()
would create a new String
using the heap as storage. Then a reference of that newly cloned String
(the added &
) is passed into call v1.insert
. So the new String
data and the reference passed to insert
will remain after the function fun1
returns.
But the rust compiler reports s1.clone()
is merely temporary.
Here are links to similar Questions, not always the same, version of this Question, but somewhat more cumbersome and verbose (IMO).
I added a Comment on those Questions that links to this Question.
Upvotes: 3
Views: 3005
Reputation: 67
Based on the second example in the answer by Alexandru Placinta, and this tutorial video A First Look at Lifetimes in Rust, I am able to come up with this version:
type Vec1<'a> = Vec::<&'a String>;
fn fun1<'a>(s1: &'a String, v1: &mut Vec::<&'a String>) {
v1.insert(0, s1);
}
fn main() {
let mut vec1 = Vec1::new();
let str1 = String::from("abcde");
let clone = str1.clone();
fun1(&clone, &mut vec1);
println!("Vector {:#?}", vec1);
// I expect both str1, and clone are still available:
println!("str1: {}", str1);
println!("clone: {}", clone);
}
Upvotes: -1
Reputation: 679
Your problem is indeed at the line indicated by the compile. Let's analyze it a little bit:
fn fun1(s1: &String, v1: &mut Vec1) {
v1.insert(0, &s1.clone());
}
Your understanding is not quite correct. Let's look at the insert's signature:
pub fn insert(&mut self, index: usize, element: T)
The type T means that it captures by value, so element won't be used after the call. You just tweaked this by making the vector a Vec<&String> not a Vec<String>.
Outcome: You want to insert a reference to a clone of the string
How it is done: you clone and insert the the reference
The difference between rust's reference and C/C++ pointers is that rust doesn't allow reference to point to deallocated data (they must always point to valid data). When you clone the string, you create a carbon copy of it, but that carbon copy is available only for the lifetime of fun1. When the call to fun1 ends, the cloned string will be dropped, because the function owned that clone, so it is responsible for cleaning it (rust's ownership based resource management).
In C++ that would have been valid: you could've allocated a pointer and push that pointer into the vector, but rust does not allow such things.
Your fun1 is equivalent to this one:
fn fun1(s1: &String, v1: &mut Vec1) {
let im_tmp = s1.clone();
v1.insert(0, &im_tmp);
}
Similar operation should always ring a bell becase im_tmp will be cleaned. To fix your issue:
type Vec1<'a> = Vec::<String>;
fn fun1(s1: &String, v1: &mut Vec1) {
v1.insert(0, s1.clone());
}
fn main() {
let mut vec1 = Vec::new();
let str1 = String::new();
fun1(&str1, &mut vec1);
println!("{:?}", vec1);
}
The type is no longer a vector of references, but instead the actual object. In fun1, the vector takes ownership of the cloned string.
Another example where your vector has the type you created, is this one, but this works because the compiler can infer the lifetime of the cloned string. Notice, if you do vec1.insert(0, &str1.clone()) won't work , because tha reference to the clone will be available only for the lifetime of the call to the insert:
type Vec1<'a> = Vec::<&'a String>;
fn main() {
let mut vec1 = Vec::new();
let str1 = String::new();
let clone = str1.clone();
vec1.insert(0, &clone);
println!("{:?}", vec1);
}
Upvotes: 5
Reputation: 629
s1.clone()
may involve some heap allocation behind the scenes, but ultimately, it also involves memory stored on the stack. For example, the length of the string and its pointer are stored on the stack. Once the method exits, this data is dropped.
If the Rust compiler didn't prevent you from doing this, v1
would contain a reference to the value created by s1.clone()
, which would be dropped after the method exists. This is a problem, because v1
would contain a reference to data that is no longer valid.
To answer your more general question about how to avoid this error (and similar errors):
This article expresses a similar view.
Upvotes: 2