Lifetime troubles sharing references between threads

A great thing about Rust is that the type-checking across functions is done solely by the function signature. That means you can replace most of the bodies of functions with unimplemented!() and preserve type-checking errors.

Repeat that process a few times, and you end up not calling a lot of functions – remove those. Inlining modules and reducing structs / enums can also help.

At some point your error will disappear – the first clue towards the problem! Keep at it, and you get a tiny reproduction:

use std::sync::{Arc, Mutex};
use std::thread;

pub enum MasterSocketList<'a> {
    One(&'a u8),
}

pub struct Slot;

impl Slot {
    pub fn new<'a>(master_socket_list: Arc<Mutex<MasterSocketList<'a>>>) -> Slot {
        thread::spawn(move || {
            master_socket_list;
        });
        unimplemented!();
    }
}

fn main() {}

Checking out the error, it still matches:

error[E0477]: the type `[closure@src/main.rs:12:23: 14:10 master_socket_list:std::sync::Arc<std::sync::Mutex<MasterSocketList<'a>>>]` does not fulfill the required lifetime
  --> src/main.rs:12:9
   |
12 |         thread::spawn(move || {
   |         ^^^^^^^^^^^^^
   |
   = note: type must satisfy the static lifetime

Let’s check the docs for the signature of thread::spawn:

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

The key point here is F: Send + 'static – the closure you give to spawn must only contain references that last the entire life of the program. This is because spawn can create threads that become detached. Once detached, the thread could live forever, so all references must live at least that long, otherwise you’d get dangling references, a bad thing! Rust saves the day, once again!

If you want to guarantee that the threads will terminate at a known point, you can use scoped threads, such as those provided by scoped-threadpool or crossbeam.

If your code didn’t have a variable with a lifetime inside of it, using some type of shared ownership like Arc paired with something that will ensure only one thread can mutate the variable, like Mutex would have been sufficient. This allows each thread to own the shared value, finally dropping it whenever the last thread exits. See How do I share a mutable object between threads? for details.

Leave a Comment