rust

error [E0716]: temporary value dropped while borrowed


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).

example code

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

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.

similar linked questions

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.


Solution

  • 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);
    }