1

I don't understand why the code below fails

use std::sync::{Arc};
use futures::lock::Mutex;

struct A<'a>{
    a: &'a u8
}

impl<'a> A<'a>{
    pub fn run(a: Arc<Mutex<Self>>) {
        let stack_thread = std::thread::Builder::new()
            .spawn(move || {
                let a = a.clone();
            }).unwrap();
    }
}

Error:

error[E0477]: the type `[closure@src/lib.rs:11:20: 13:14]` does not fulfill the required lifetime
  --> src/lib.rs:11:14
   |
11 |             .spawn(move || {
   |              ^^^^^
   |
   = note: type must satisfy the static lifetime

Playground

Actually I have an idea. If I take the lifetimes of A and remove the a member, it works. So it is possibly rejecting a: Arc<Mutex<Self>> inside of the spawned thread. When you spawn a thread, the closure must have a static lifetime:

pub fn spawn<F, T>(f: F) -> JoinHandle<T> 
    where F: FnOnce() -> T, 
          F: Send + 'static, 
          T: Send + 'static

so it looks like the fact that a is passed inside of the closure, makes the closure be not 'static. But I don't see why, as a: Arc<Mutex<Self>> inside the thread lives as long as the closure. Could somebody explain in details what is happening? I'm confusing lifetime of things with lifetime of references, etc.

Gatonito
  • 1,662
  • 5
  • 26
  • 55
  • I _think_ there's nothing stopping your code from destroying the Arc and keeping the `&'a u8` reference in the thread, so the Arc doesn't guarantee that the thread won't reference something that has been freed. – loganfsmyth May 14 '21 at 21:24
  • Is https://stackoverflow.com/questions/32750829/how-can-i-pass-a-reference-to-a-stack-variable-to-a-thread what you're looking for, or do you actually want a persistent thread in the background referencing this `u8`? It isn't clear from your code why you want the reference so it is hard to suggest alternatives. – loganfsmyth May 14 '21 at 21:25

1 Answers1

1

While the Arc<Mutex<Self>> is moved into the closure and lives for as long as the closure is alive (dropping the closure drops the Arc), this is not sufficient here. As you have seen in the docs, the newly spawned thread could live forever ('static), and it requires the passed-in closure to be 'static. But just wrapping something into an Arc does not make that thing 'static:

If I give you some generic A<'a>, as part of Arc<Mutex<Self>>, with ANY lifetime 'a, that lifetime 'a could be shorter than 'static. If you put this A<'a> into an Arc and pass it along, the destruction of the thing of lifetime 'a (the a-member in this case) would make A, Mutex<A> and Arc<Mutex<A>> invalid.

In practical terms: Generic lifetimes can't be "solved" by wrapping them in atomic primitives, as they can't enforce a 'static lifetime on their inhabitant.

What you are looking for is either

impl A<'static>{
    pub fn run(a: Arc<Mutex<Self>>) {
        
        let stack_thread = std::thread::Builder::new()
            .spawn(move || {
                let a = a.clone();
            }).unwrap();
    }
}

or you need to change the definition of A to be 'static in any case, e.g. by holding a Box<[u8]> instead of a &'a [u8].

user2722968
  • 13,636
  • 2
  • 46
  • 67
  • Also note that the `let a = a.clone()` line is unneeded, as you already receive ownership for that argument. You don't need to clone, you just need to move: A `let a = a;` inside the closure will do that. – user2722968 May 14 '21 at 21:32
  • Does the `'static` mean that when the stack_thread stops, and no other copy of the `a: Arc>` exists, the object still lives in memory? I always get confused at this – Gatonito May 15 '21 at 01:53
  • No, and this is often a source of confusion: `'static` does NOT mean that a value lives forever. It just means that the value _can be made_ to live forever. That is, `stack_thread` must be able _to make_ all things passed to it live forever if it so chooses, because it itself may run forever. It can make things live forever by simply not destroying those values. But if you pass some `A<'a>` for ANY `'a`, this may not be possible, because `'a` may be shorter than whatever `stack_thread` needs. `Arc` can't fix that. – user2722968 May 15 '21 at 10:37
  • This is why a `Vec`, a `Box<[u8]` or a `Arc<[u8]>` are `'static` and can be passed: Simply not destroying them is enough to make them live as long as we want. But a `&'a [u8]` has some - possibly shorter - lifetime `'a` at which point we _have_ to destroy it. – user2722968 May 15 '21 at 10:39