Why is T 'static constrained when using Arc<T> and thread::spawn


#1

I have hit a speedbump in a project I am working on.

Basically, I have an Arc<T> cloned object that gets (move) captured in a closure, and that closure gets moved to another thread with thread::spawn. The compiler complains that T needs to be 'static but I don’t see why that is necessary, as Arc<T> should be managing the lifetime. I don’t want to add the 'static constraint to T all throughout my code.

I have created a dummy example below. I have extracted the various functions to highlight the constraints placed on the callback and T type.

I think I might be misunderstanding the role of Arc, could someone please explain why the 'static lifetime is required for T?

use std::sync::Arc;
use std::thread;

fn main() {
	let my_struct = Arc::new(MyStruct);

	let arc_clone = Arc::clone(&my_struct);

	call_spawn_thread_code(arc_clone);
}

fn call_spawn_thread_code<T>(t: Arc<T>)
where
	T: MyTrait + Send + Sync, // + 'static <-- will fix the issue but means T must be 'static, which propogates
{
	spawn_thread_code(move |string| {
		t.foo();
		println!("{}", string);
	});
}

fn spawn_thread_code<F>(cb: F)
where
	F: Fn(&str) + Send + 'static,
{
	let jh = thread::spawn(move || cb("Hello, world!"));

	jh.join().unwrap();
}

struct MyStruct;

impl MyTrait for MyStruct {}

trait MyTrait {
	fn foo(&self) {
		println!("bar");
	}
}


#2

T: 'static means the type T doesn’t contains any reference, except the 'static ones. For example, Arc<&'a i32> can’t outlive the lifetime 'a even though it’s wrapped in Arc.


#3

Note that this in turn is required to be able to safely implement Send and Sync for Arc, which is mainly used for multithreaded situations


#4

Send and Sync are distinct concerns from the lifetime – Arc<T> only requires T: Send + Sync for both. The 'static requirement comes from the unscoped implementation of std::thread::spawn, but you can use non-'static types perfectly well with rayon, crossbeam scope, etc.


#5

T: 'static means the type T doesn’t contains any reference, except the 'static ones. For example, Arc<&'a i32> can’t outlive the lifetime 'a even though it’s wrapped in Arc.

Okay, that makes sense. I think I was getting confused with putting a lifetime parameter on type T with T: 'static thinking it would act in a similar manner to &'static. It makes sense that if T was a type that contained references it would need to adhere to the lifetime constraints of subsequent functions.

Thanks for the help!


#6

Burned by this issue for a whole day until I saw this thread: Why does thread::spawn need static lifetime for generic bounds?