# Scoped threads All the lifetime issues we discussed so far have a common source: the spawned thread can outlive its parent.\ We can sidestep this issue by using **scoped threads**. ```rust let v = vec![1, 2, 3]; let midpoint = v.len() / 2; std::thread::scope(|scope| { scope.spawn(|| { let first = &v[..midpoint]; println!("Here's the first half of v: {first:?}"); }); scope.spawn(|| { let second = &v[midpoint..]; println!("Here's the second half of v: {second:?}"); }); }); println!("Here's v: {v:?}"); ``` Let's unpack what's happening. ## `scope` The `std::thread::scope` function creates a new **scope**.\ `std::thread::scope` takes a closure as input, with a single argument: a `Scope` instance. ## Scoped spawns `Scope` exposes a `spawn` method.\ Unlike `std::thread::spawn`, all threads spawned using a `Scope` will be **automatically joined** when the scope ends. If we were to "translate" the previous example to `std::thread::spawn`, it'd look like this: ```rust let v = vec![1, 2, 3]; let midpoint = v.len() / 2; let handle1 = std::thread::spawn(|| { let first = &v[..midpoint]; println!("Here's the first half of v: {first:?}"); }); let handle2 = std::thread::spawn(|| { let second = &v[midpoint..]; println!("Here's the second half of v: {second:?}"); }); handle1.join().unwrap(); handle2.join().unwrap(); println!("Here's v: {v:?}"); ``` ## Borrowing from the environment The translated example wouldn't compile, though: the compiler would complain that `&v` can't be used from our spawned threads since its lifetime isn't `'static`. That's not an issue with `std::thread::scope`—you can **safely borrow from the environment**. In our example, `v` is created before the spawning points. It will only be dropped _after_ `scope` returns. At the same time, all threads spawned inside `scope` are guaranteed to finish _before_ `scope` returns, therefore there is no risk of having dangling references. The compiler won't complain!