This is similar to Parameter type may not live long enough?, but my interpretation of the solution doesn't seem to be working. My original boiled-down test case is:
use std::fmt::Debug;
use std::thread;
trait HasFeet: Debug + Send + Sync + Clone {}
#[derive(Debug, Clone)]
struct Person;
impl HasFeet for Person {}
#[derive(Debug, Copy, Clone)]
struct Cordwainer<A: HasFeet> {
shoes_for: A,
}
impl<A: HasFeet> Cordwainer<A> {
fn make_shoes(&self) {
let cloned = self.shoes_for.clone();
thread::spawn(move || {
println!("making shoes for = {:?}", cloned);
});
}
}
This gives me the error:
error[E0310]: the parameter type `A` may not live long enough
--> src/main.rs:19:9
|
16 | impl<A: HasFeet> Cordwainer<A> {
| -- help: consider adding an explicit lifetime bound `A: 'static`...
...
19 | thread::spawn(move || {
| ^^^^^^^^^^^^^
|
note: ...so that the type `[closure@src/main.rs:19:23: 21:10 cloned:A]` will meet its required lifetime bounds
--> src/main.rs:19:9
|
19 | thread::spawn(move || {
| ^^^^^^^^^^^^^
Instead of making A
'static
, I go through and add an explicit lifetime to the HasFeet
trait:
use std::fmt::Debug;
use std::thread;
trait HasFeet<'a>: 'a + Send + Sync + Debug {}
#[derive(Debug, Copy, Clone)]
struct Person;
impl<'a> HasFeet<'a> for Person {}
struct Cordwainer<'a, A: HasFeet<'a>> {
shoes_for: A,
}
impl<'a, A: HasFeet<'a>> Cordwainer<'a, A> {
fn make_shoes(&self) {
let cloned = self.shoes_for.clone();
thread::spawn(move || {
println!("making shoes for = {:?}", cloned);
})
}
}
This now gives me the error:
error[E0392]: parameter `'a` is never used
--> src/main.rs:11:19
|
11 | struct Cordwainer<'a, A: HasFeet<'a>> {
| ^^ unused type parameter
|
= help: consider removing `'a` or using a marker such as `std::marker::PhantomData`
I think that 'a
is used as the lifetime parameter to the HasFeet
trait. What am I doing wrong here?
We can also add lifetime parameters as constraints on generic types, and these are called lifetime bounds. Lifetime bounds help Rust verify that references in generic types won’t outlive the data they’re referencing. For an example, consider a type that is a wrapper over references.
In our definition of Parser, in order to say that 's (the lifetime of the string slice) is guaranteed to live at least as long as 'c (the lifetime of the reference to Context ), we change the lifetime declarations to look like this:
In the angle brackets where we declare lifetime parameters, we can declare a lifetime 'a as usual, and declare a lifetime 'b that lives at least as long as 'a by declaring 'b with the syntax 'b: 'a.
The default lifetime of a trait object is 'static. With &'a Trait or &'a mut Trait, the default lifetime is 'a. With a single T: 'a clause, the default lifetime is 'a. With multiple T: 'a -like clauses, there is no default; we must be explicit.
The std::thread::spawn()
function is declared with a Send + 'static
bound on its closure. Anything which this closure captures must satisfy the Send + 'static
bound. There is no way around this in safe Rust. If you want to pass data to other threads using this function, it must be 'static
, period.
It is possible to lift the 'static
restriction with the proper API, see How can I pass a reference to a stack variable to a thread? for an example.
However, a 'static
bound is not as scary as it may seem. First of all, you cannot force anything to do anything with lifetime bounds (you can't force anything to do anything with any kind of bounds). Bounds just limit the set of types which can be used for the bounded type parameter and that is all; if you tried to pass a value whose type does not satisfy these bounds, the compiler will fail to compile your program, it won't magically make the values "live longer".
Moreover, a 'static
bound does not mean that the value must live for the duration of the program; it means that the value must not contain borrowed references with lifetimes other than 'static
. In other words, it is a lower bound for possible references inside the value; if there are no references, then the bound does not matter. For example, String
or Vec<u64>
or i32
satisfy the 'static
bound.
'static
is a very natural restriction to put on spawn()
. If it weren't present, the values transferred to another thread could contain references to the stack frame of the parent thread. If the parent thread finishes before the spawned thread, these references would become dangling.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With