Skip to main content

scope

Function scope 

Source
pub fn scope<'env, F, R>(f: F) -> R
where F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> R,
Expand 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);
});