Generic scopes

available on nightly stabilization

Summary

APIs like std::thread::scope follow a "scope" pattern:

#![allow(unused)]
fn main() {
in_scope(|scope| {
    ... /* can use `scope` in here */ ...
})
}

In this pattern, the closure takes a scope argument whose type is somehow limited to the closure body, often by including a fresh generic lifetime ('env, in the case of [std::thread::scope]). The closure is then able to invoke methods on scope. This pattern makes sense when there is some setup and teardown required both/after the scope (e.g., blocking on all the threads that were spawned to terminate).

The "generic scopes" pattern encapsulates this "scoped closure" concept:

#![allow(unused)]
fn main() {
owner.with_scope(|scope| {
    ...
})
}

Here, the type of the scope that the closure will depend on the with_scope call, but it needs to include some fresh lifetime that is tied to the with_scope call itself.

Details

The generic scopes pattern arise from smithay and was demonstrated by this playground) snippet.

In this case, the "owner" object is a renderer, and the "scope" call is called render. Clients invoke...

#![allow(unused)]
fn main() {
r.render(|my_frame| { ... })
}

...where the precise type of my_frame depends on the renderer. Frames often include thread-local information which should only be accessible during that callback.