Your closure captures a string slice, therefore its environment has lifetime no longer than that of this slice, but thread::spawn()
requires its argument to have static lifetime:
pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static
(note the F: 'static
requirement)
This is necessary because when the thread spawned by thread::spawn()
gets to run, the string from which the slice is taken may already be destroyed. Rust has actually prevented an error in your code!
There are several ways to fix it.
1) The simplest way would be to clone the string for each thread:
fn start(host: &str) {
{
let host = host.to_owned();
thread::spawn(move || testf(&host));
}
{
let host = host.to_owned();
thread::spawn(move || testf(&host));
}
}
This way each thread receives its own copy of the string which will be destroyed when the thread itself finishes.
2) If you know that your threads should finish before start()
function ends, you can use a third-party librariy like crossbeam to pass references into spawned threads:
extern crate crossbeam;
fn start(host: &str) {
crossbeam::scope(|scope| {
scope.spawn(move || testf(host));
scope.spawn(move || testf(host));
});
}
This way start()
will wait until both threads spawns in scoped()
has finished before returning, making sure that whatever string host
points to won't be destroyed prematurely.
Previously such functionality was included in the standard library, but the way it was implemented was found to be unsound, so it was deprecated; a proper replacement for this functionality is yet to be added back into the standard library.
3) Even another alternative would be to use Arc<String>
to share the string between threads, but this would require more significant changes outside of start()
:
use std::sync::Arc;
fn start(host: Arc<String>) {
{
let host = host.clone();
thread::spawn(move || testf(&host));
}
{
let host = host.clone();
thread::spawn(move || testf(&host));
}
}
With this approach you need to keep your string inside an Arc
(which is an "atomically reference counted" pointer), so this requires you to change the code which calls start()
. Cloning is probably better. Of course, if you want to share not &str
but &SomeStruct
where SomeStruct
is large and/or not cloneable, there is no way to avoid scoping xor Arc
.