Threads

Creating Threads with thread.spawn

std::thread::spawn creates a new native operating-system thread that executes a closure F concurrently with the calling thread and returns a JoinHandle<T> representing the spawned thread's lifecycle and return value.

Function Signature and Trait Bounds

pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
    F: FnOnce() -> T + Send + 'static,
    T: Send + 'static,
Bound Requirement Why It Is Required
F: FnOnce() -> T Closure is called once and returns T Each spawned thread executes its entry point once from start to finish
F: Send Closure environment can cross threads All captured variables are transferred from the parent thread to the child thread
F: 'static Closure holds no non-'static references A detached thread can outlive the stack frame that spawned it
T: Send + 'static Return value can be sent back across threads .join() moves T from the child thread back to the joining thread
Parent Thread (main)                       OS Kernel / Child Thread
+-----------------------------------+      +-----------------------------------+
| let handle = thread::spawn(|| {   +----> | Allocates 2 MiB stack frame       |
|     compute_work()                |      | Runs `compute_work()` concurrently|
| });                               |      | Stores return value `T`           |
| // Continues executing in parallel|      +-----------------+-----------------+
| let res = handle.join().unwrap(); | <----------------------+
+-----------------------------------+

Spawning Threads with thread::spawn vs. thread::Builder

While thread::spawn(f) uses the default stack size (2 MiB, or the RUST_MIN_STACK environment variable) and an unnamed thread, std::thread::Builder customizes the thread's human-readable name (shown in OS debuggers, top/htop, and panic messages), stack size, and returns an io::Result<JoinHandle<T>> instead of panicking if the OS refuses to create a thread:

Spawning Method Custom Thread Name? Custom Stack Size? Behavior on OS Thread Creation Failure
thread::spawn(f) No (<unnamed>) Default (2 MiB) Panics (failed to spawn thread)
thread::Builder::new().spawn(f) Yes (.name("worker-1".into())) Yes (.stack_size(4 * 1024 * 1024)) Returns Err(std::io::Error)
use std::thread;

fn main() {
    // 1. Standard `thread::spawn`:
    let default_handle = thread::spawn(|| {
        format!(
            "unnamed thread (name = {:?})",
            thread::current().name()
        )
    });

    // 2. Configured `thread::Builder` with custom name and 4 MiB stack:
    let named_handle = thread::Builder::new()
        .name(String::from("indexer-worker"))
        .stack_size(4 * 1024 * 1024)
        .spawn(|| {
            format!(
                "named thread (name = {:?})",
                thread::current().name()
            )
        })
        .expect("OS failed to spawn thread");

    println!("{}", default_handle.join().unwrap());
    println!("{}", named_handle.join().unwrap());
}
unnamed thread (name = None)
named thread (name = Some("indexer-worker"))

What Happens When main Exits Before Spawned Threads Finish

When the main thread of a Rust program finishes fn main(), the operating system terminates the entire process immediately—stopping any still-running background threads mid-execution without unwinding their stacks. Always call .join() on JoinHandle<T> when you need a background thread's work and destructors to complete before exiting main.