Skip Navigation

How to think about function trait bounds (specifically that for thread::spawn())?

doc.rust-lang.org spawn in std::thread - Rust

Spawns a new thread, returning a `JoinHandle` for it.

This pertains to Ch 16.03 of The Book, specifically Arc in multithreaded programs

I was just looking at the signature for thread::spawn (documentation linked to by post URL) wondering if and how the requirement for a thread-safe smart pointer is enforced by the type system. In short, how is the use of Arc necessitated by the type system??

For the signature, you get this:

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

Where the parameter f is bound by F: FnOnce() -> T + Send + 'static.

And ... I'm not entirely sure how I should think about this.

Obviously, Send and 'static here are related to my question, where in some way the closure must be thread-safe (through the Send trait) and also have a whole-life-of-the-program lifetime (through the 'static lifetime). Therefore, in some way, something like Rc would be invalid but Arc would be.

But how exactly?

  1. Are the bounds on the return type of the function/closure, which then sort of back propagate onto the input parameters or captured values of the function/closure?
  2. Or are these somehow properties of the function itself?
  3. And why is T separately bound by Send + 'static when they are already present in the bounds on F?

My best guess is that the bounds on F are best parsed as ...

F: ( FnOnce() -> T ) + Send + 'static
T: Send + 'static

IE, Everything separated by a + is unitary, and, therefore, FnOnce() -> T is a single bound and Send another separate bound.

But I'm unsure about that, haven't read that anywhere, and am not really sure how to understand how exactly a function can have Send or 'static without some logic linking that directly to its input parameters?

Thoughts?

4

You're viewing a single thread.

4 comments