Generic scopes
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.