pub fn scope<'env, F, R>(f: F) -> RExpand description
Run a closure that can spawn short-lived goroutines borrowing local data.
scope is the goroutine equivalent of std::thread::scope. The
closure receives a &Scope handle; goroutines spawned via
Scope::go may borrow any data that is alive in the caller’s
environment ('env). All spawned goroutines are guaranteed to finish
before scope returns.
The return value of the outer closure is propagated to the caller.
§Scheduling
The wg.wait() at the end of scope uses goroutine-level parking: the
calling goroutine yields to the scheduler (the M and P remain free to run
other goroutines, including the scoped ones). No OS thread is blocked.
§Panics in the outer closure
If the closure passed to scope panics, scope still waits for every
already-spawned goroutine to finish. The panic is then re-raised via
std::panic::panic_any on the current OS thread — ensuring the correct
landing pad is found even if the goroutine was rescheduled during
wg.wait(). Note: this causes the panic hook to fire a second time;
the message will appear in stderr, but the panic itself behaves correctly.
§Panics in a scoped goroutine
A goroutine panic is delivered to ScopedJoinHandle::join as Err(payload).
If the handle is dropped without joining, the panic payload is silently
discarded.
§Example — parallel slice reduction
go_lib::run(|| {
let data = vec![1_i64, 2, 3, 4, 5];
let sum = go_lib::scope(|s| {
let h1 = s.go(|| data[..3].iter().sum::<i64>());
let h2 = s.go(|| data[3..].iter().sum::<i64>());
h1.join().unwrap() + h2.join().unwrap()
});
assert_eq!(sum, 15);
});§Example — fire-and-forget goroutines with shared stack state
use std::sync::atomic::{AtomicI32, Ordering};
go_lib::run(|| {
let counter = std::sync::atomic::AtomicI32::new(0);
go_lib::scope(|s| {
for _ in 0..8 {
s.go(|| { counter.fetch_add(1, Ordering::Relaxed); });
}
// scope blocks here until all 8 goroutines have finished
});
assert_eq!(counter.load(Ordering::SeqCst), 8);
});