Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parameter type may not live long enough (with threads)

Tags:

rust

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?

like image 215
Watts Avatar asked Aug 18 '15 22:08

Watts


People also ask

What is a lifetime parameter in rust?

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.

What is the lifetime of a parser?

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:

How do you declare a lifetime parameter in Python?

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.

What is the default lifetime of a trait object?

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.


1 Answers

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.

like image 185
Vladimir Matveev Avatar answered Sep 28 '22 12:09

Vladimir Matveev