Task[T] is the typed handle for a spawned computation that will produce T.
Application code normally obtains one from spawn.
task: Task[int] = spawn { calculate_total() }The handle carries the result type but does not contain the result immediately. The child owns its execution frame until it completes and the parent consumes that result.
Joining
Call .await() to wait for completion and retrieve the result:
value: int = task.await()await is a method, not a keyword. It has three important properties:
- it may suspend the caller while the child is running;
- it returns the child’s
T; - it consumes and reaps the completed task handle.
Treat a task as single-consumer. Do not await it twice, put the same task in
multiple select arms, or use it after a selection consumed it.
If the child already completed, await() takes a synchronous fast path. The
method still has a static Suspend effect because the caller cannot generally
know completion state.
Wide results are copied out before the child’s frame is reaped. That transport
detail is invisible to source code; returning a struct, tuple, list, or string
uses the same Task[T] contract as returning an integer.
Completion order
Joining tasks in source order does not make their work sequential if all tasks were spawned first:
left := spawn { fetch_left() }
right := spawn { fetch_right() }
left_value := left.await()
right_value := right.await()Both children are eligible to run before the first join completes. The joins only choose the order in which the parent retrieves their values.
By contrast, this starts the second call only after the first finishes:
left_value := (spawn { fetch_left() }).await()
right_value := (spawn { fetch_right() }).await()Spawn all independent work first when overlap is intended.
Result channels
Task[T] preserves whatever type the body returns. A child whose ordinary
value is Result[Record, LoadError] produces
Task[Result[Record, LoadError]]; awaiting returns that result for normal
matching or ? propagation.
Task cancellation is not encoded as an automatic Err member of T. Define an
application error only when cancellation itself belongs to that API’s domain
contract.
Raw handles
Task[T].raw_handle() exposes the scheduler handle as usize. It exists for
the compiler’s selectable protocol and low-level runtime integration:
handle: usize = task.raw_handle()Ordinary application code should retain the typed task instead. A raw handle does not transfer the type, result ownership, or reaping rules.
Do not persist, serialize, compare for business identity, or reconstruct
Task[T] from a raw handle. It is an arena-relative scheduler token whose
encoding belongs to the runtime ABI.
Available methods
The current built-in surface is intentionally small:
| Method | Result | Purpose |
|---|---|---|
await() |
T |
Join, retrieve, and reap |
cancel() |
void |
Request cooperative cancellation |
raw_handle() |
usize |
Runtime/select integration |
There is no stable is_complete, timeout, detach, or multi-await API on
Task[T] today. Build timeouts by racing a task against an appropriate timer
source once that source is available in the target runtime.
Ownership
Dropping the source-level handle does not detach the child from the parent task tree. The runtime can still discover it for subtree cancellation and cleanup. However, without a handle the parent cannot retrieve its result or request targeted cancellation.
Keep the handle when completion, errors, or shutdown matter. Discard it only for deliberately best-effort child work with an explicit failure policy.