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:(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:
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:This way
start()
will wait until both threads spawns inscoped()
has finished before returning, making sure that whatever stringhost
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 ofstart()
: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 callsstart()
. Cloning is probably better. Of course, if you want to share not&str
but&SomeStruct
whereSomeStruct
is large and/or not cloneable, there is no way to avoid scoping xorArc
.Declaration of thread::spawn function tried to tell me about my problem :)
so, I can fix that using (host: &'static str) insted of (host: &str)
it works great for me