I can run this code
fn testf(host: &str) {}
fn start(host: &str) {
testf(host);
testf(host);
}
but for some reason, I can't run this one:
fn testf(host: &str) {}
fn start(host: &str) {
thread::spawn(move || testf(host));
thread::spawn(move || testf(host));
}
because of the following error
src/server.rs:30:5: 30:18 error: the type `[closure@src/server.rs:30:19: 30:38 host:&str]` does not fulfill the required lifetime
src/server.rs:30 thread::spawn(move || testf(host));
^~~~~~~~~~~~~
note: type must outlive the static lifetime
error: aborting due to previous error
Can somebody explain me, what is wrong with it and how to fix it?
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
.
Declaration of thread::spawn function tried to tell me about my problem :)
pub fn spawn<F, T>(f: F) -> JoinHandle<T> where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static
so, I can fix that using (host: &'static str) insted of (host: &str)
fn testf(host: &str) {}
fn start(host: &'static str) {
thread::spawn(move || testf(host));
thread::spawn(move || testf(host));
}
it works great for me