Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

error [E0716]: temporary value dropped while borrowed

Tags:

rust

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

  • error[E0716]: temporary value dropped while borrowed
  • Why is it legal to borrow a temporary?
  • temporary value dropped while borrowed
  • Temporary value dropped while borrowed (E0506)
  • "Temporary value dropped while borrowed" when using string.replace()
  • error[E0716]: temporary value dropped while borrowed
  • How to solve "temporary value dropped while borrowed"
  • Iterator Find, Temporary Value Dropped While Still Borrowed
  • "temporary value dropped while borrowed" with capturing closure
  • Rust reference dropped here while still borrowed
  • Confused by "temporary value dropped here while still borrowed" [duplicate]
  • "borrowed value does not live long enough" when using the builder pattern
  • Builder pattern - borrowed value does not live long enough
  • Unable to compile a Rust builder pattern because a borrowed value does not live long enough [duplicate]
  • Borrowed value does not live long enough

I added a Comment on those Questions that links to this Question.

like image 851
JamesThomasMoon Avatar asked Oct 14 '25 19:10

JamesThomasMoon


2 Answers

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);
}
like image 108
Alexandru Placinta Avatar answered Oct 17 '25 21:10

Alexandru Placinta


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

  • As soon as you have explicit lifetime parameters – unless you have very good reason to do so and know exactly what you're doing – you're likely heading down a rabbit hole and will up in an endless tango with the borrow checker.
  • Learn about smart pointers, which are (in general) necessary to allocate values on the heap.

This article expresses a similar view.

like image 20
Nicholas Weston Avatar answered Oct 17 '25 21:10

Nicholas Weston



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!