Expand description
A scoped wrapper around tokio::task::JoinSet allowing non-'static
futures to be spawned safely.
§What ScopedJoinSet provides
Tokio requires all spawned tasks to be 'static. ScopedJoinSet lifts
this restriction by:
- allocating each user future and its output slot on the heap,
- pinning the wrapper future so it never moves,
- unsafely casting it to
'static(sound because the allocation is kept alive until the task completes), - storing each task’s output in
Box<Option<T>>, - retrieving and deallocating that output when the task completes.
This lets users write:
§Example
use scoped_join_set::ScopedJoinSet;
#[tokio::main]
async fn main() {
let temporary_value = Box::new(5);
let value_ref = &temporary_value;
let mut set = ScopedJoinSet::<u32>::new();
set.spawn(async {
println!("I have a {value_ref}");
**value_ref
});
while let Some(result) = set.join_next().await {
assert_eq!(result.unwrap(), *temporary_value);
}
set.shutdown().await;
}without requiring the future to be 'static.
§How it works internally
spawn()allocates aBox<MaybeUninit<T>>for the result and leaks it.- The user future is wrapped in
WriteOutput, which writes its output into this slot. - The wrapper future is pinned and unsafely converted to
'static. - Tokio polls the wrapper and returns a raw pointer identifying the output slot.
join_next()converts that pointer back, reads the output, and frees the allocation.
No hashmaps or external bookkeeping structures are used—the pointer
returned by each completed task uniquely identifies its output slot.
There is no self-referential struct and no dependence on pinning beyond
ensuring that the wrapped future remains immovable.
§Important: Explicit Shutdown Required
A ScopedJoinSet must be terminated explicitly using either
before it is dropped.
Dropping a ScopedJoinSet implicitly (e.g., letting it go out of scope
without calling one of the methods above) is considered a logic error.
To prevent accidental misuse — which could otherwise cause tasks holding references into the stack to outlive their scope — the destructor prints an error message and aborts the process.
Structs§
- Scoped
Join Set - A scoped task set analogous to
tokio::task::JoinSetbut accepting non-'staticfutures.
Enums§
- Join
Error - Errors returned by scoped tasks.