How can I use a borrowed temporary value in multithreads?

In the following example, the compiler throws an error of "temporary value dropped while borrowed" that does not actually exist.
Code:

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let m: Arc<Mutex<Vec<_>>> = Arc::new(Mutex::new(vec![]));
    let str = &String::from("This is thread");
    let mut threads = vec![];
    for i in 1..10 {
        let m = m.clone();
        let t = thread::spawn(move || {
            let mut names = m.lock().unwrap();
            names.push(format!("{}: {}", &str, i));
        });

        threads.push(t);
    }

    for t in threads {
        t.join().unwrap();
    }

    let names = m.lock().unwrap();
    println!("{:?}", names);
}

Error:

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:6:16
   |
6  |       let str = &String::from("This is thread");
   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
...
10 |           let t = thread::spawn(move || {
   |  _________________-
11 | |             let mut names = m.lock().unwrap();
12 | |             names.push(format!("{}: {}", &str, i));
13 | |         });
   | |__________- argument requires that borrow lasts for `'static`
...
24 |   }
   |   - temporary value is freed at the end of this statement

For more information about this error, try `rustc --explain E0716`.

However, I can make sure that the borrow of str isn't freed as I called std::thread::JoinHandle::join() method, before it reaches the end of fn main().
How can I solve this problem without cloning str in every iteration of loop?

Use scope in std::thread - Rust.

2 Likes

Yes, it does. In terms of lifetimes, any reference captured by the closure passed into thread::spawn must have 'static lifetime. However, clearly str doesn't have a 'static lifetime since it is borrowed from a temporary.
Even if you do something like:

let str = String::from("This is thread");
let str = &str

this problem won't be solved.

What you need is called "scoped threads", and this can be obtained via thread::scope.

First off, this hasn't got much to do with threads. Taking a reference to a temporary is an error by itself.

You shouldn't create a string and then reference it immediately – this doesn't store the string "by reference", that's not what references do (unlike other languages). References are temporary pointers to data that is already owned by someone else, so it makes no sense to create a reference to something that is not already stored in a variable. You should store your string itself in a variable, and then take a reference to it when needed.

Second, the type system can't encode the fact that you happened to call join(). The thread might have as well outlived the str. If you want to use variables from the enclosing scope, use thread::scoped() instead.

2 Likes

Thanks. I got it.

The code in question[1] does qualify for temporary lifetime extension though. I don’t like code relying on temporary lifetime extension all that much either, but your explanation that this kind of code is “an error by itself” isn’t accurate nonetheless.


@Yankang, in case you’re interested in seeing a usage of thread::scope in this context:

Original code with minimal modification to add thread::scope.

Removing the need for the additional threads vector and .join().unwrap() loop by relying on the scope waiting for all the threads on exit anyways.


  1. i.e.: let str = &String::from("This is thread"); ↩︎

2 Likes

Right, but I don't want to teach that.

There's nothing inaccurate in it. When the expression happens not to be eligible for lifetime extension, it fails to compile, or leads to UB when used in an unsafe context. Furthermore, as mentioned above, I explicitly don't want to reinforce the notion that this is OK to do, because it really isn't, even if it happens to compile. By "error", I didn't necessarily mean a compilation error, but a more general notion of something that shouldn't be practiced/relied on.


As an aside, I repeatedly observed recently that you keep pointing out things to me that I am obviously aware of. (And you should know that, because we both have been participating in this forum for several years.) This is annoying and has no value – please don't do this.

To me, your answer reads as if the error message is not due to the use of the reference in the thread::spawn closure and instead because of the temporary’s lifetime being too short.

However, the code example does indeed only not compile because of the usage of thread::spawn, as e.g. demonstrated by the minimal change to thread::scope usage that makes it compile.

As mentioned before, I agree that relying on temporary lifetime extension for no reason is not nice code; others probably won’t – after all, otherwise: why does the feature exist at all? – but I find it slightly more confusing than useful.

That’s a fair interpretation, though IMO, in the context you stated it in not necessarily the most natural one that readers of your reply would think of.


I am naturally not replying to educate you in particular, but mainly to add additional information for others to read. :slight_smile: Either information that I think should be added, or points that I thought might’ve been potentially misleading inaccuracies.

The technicalities of this forum means that amending or addressing something someone else said most naturally happens by using the “quote” feature and building a reply. (Even if it wasn’t a reply, you’d be notified nonetheless from the quotation.) With the context in mind that I am replying not to educate you in particular but to leave a response that other readers can lead, I am of course open for suggestions on how to be less “annoying”!

1 Like