Course contentsShow
← Rust
Lesson 138 of 1,9102. The Ownership ModelFree lesson

Scoped Threads and Lifetime Guarantees

Execute concurrent tasks that reference stack data safely within a bounded lifetime.

In this lesson, you will learn how to safely share stack-allocated data with concurrent threads by using scoped threading to enforce strict lifetime boundaries.

The Problem: Thread Lifetimes

When you spawn a standard thread using std::thread::spawn, the Rust compiler requires that the closure you pass in has a 'static lifetime. This is because the thread might outlive the function that created it, and the compiler cannot guarantee that the variables you borrowed from your stack will still exist when the thread finally executes. This forces you to move ownership or use reference counting (Arc) for everything.

The Solution: Scoped Threads

Scoped threads change the rules. By using std::thread::scope, you tell the compiler: "These threads are guaranteed to finish before the scope ends." Because the scope acts as a join point—forcing all threads to complete before moving to the next line of code—you no longer need a 'static lifetime. You can safely borrow data from your current stack frame and pass it into the threads.

Consider this example:

let numbers = vec![1, 2, 3];

std::thread::scope(|s| {
    // We can borrow 'numbers' because the scope guarantees 
    // the threads will finish before 'numbers' goes out of scope.
    s.spawn(|| {
        println!("The first number is: {}", numbers[0]);
    });

    s.spawn(|| {
        println!("The length is: {}", numbers.len());
    });
});

// Once we reach this point, all threads in the scope are finished.
println!("All threads complete!");

Why it works

The s.spawn method uses the scope's lifetime to ensure the threads cannot outlive the data they borrow. If you tried to return a reference to numbers outside of that scope, the compiler would stop you because the borrow checker sees the threads are tied to the scope block. This allows you to work with local stack data in parallel without the overhead of Arc or the complexity of manual lifetime management.

Key Takeaway: Scoped threads allow you to borrow local stack data in concurrent tasks by guaranteeing that all spawned threads must complete before the scope block exits.