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

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");
	}
}

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.

2 Likes

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

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.

4 Likes

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!

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

2 Likes