Future

Cold async futures, structured concurrency, timers, readiness, and scoped tasks.

std.future is the canonical async module. Async calls return cold Future<T> values; work starts when a future is consumed by await or by a structured future API.

import std.future;

async fn load() -> i32 {
    return 42;
}

async fn main() {
    let work: future.Future<i32> = load();
    let value = await work;
    assert value == 42;
}

Future<T> is move-only and must be consumed before it leaves scope. Use std.drop(future_value) to explicitly discard a cold future without running its body.

Core Types

TypePurpose
Future<T>Cold async value returned by async calls and future helpers.
Task<T>Scoped hot runtime handle produced by structured APIs such as group.next().
Group<T, E>Scoped structured-concurrency result stream.
AsyncErrorCancelled and Timeout.

User code cannot manually construct Task<T>. It is a scoped handle exposed by structured APIs, not the normal async-call result.

Helpers

future.ready(value), future.sleep(duration), future.yield(), future.readable(fd), and future.writable(fd) all return cold futures. Creating or dropping them does not touch the scheduler, timer heap, or file descriptor readiness state.

import std.future;
import std.time;

async fn main() {
    await future.yield();
    await future.sleep(try time.ms(1));
    let value = await future.ready(7);
    assert value == 7;
}

future.readable(fd) and future.writable(fd) wait for readiness only. The following I/O call still observes the real operating-system result.

future.yield() is a cooperative suspension point. It lets the current runtime poll other ready work, but it does not promise fairness, starvation freedom, exact poll counts, or a particular batching strategy.

Structured Futures

future.all, future.race, and future.timeout are also cold until awaited.

FunctionPurpose
future.all(futures...)Start all children and wait for all of them. Child failures cancel unfinished siblings.
future.race(futures...)Start all children and return the first observed homogeneous child result.
future.timeout(future, duration)Start a child and a deadline, returning the child result or AsyncError.Timeout.

Structured APIs that can cancel children include future.AsyncError in their returned error set.

import std.future;

async fn a() -> i32 {
    return 10;
}

async fn b() -> i32 {
    return 32;
}

async fn main() {
    let pair = (await future.all(a(), b())) catch { _ => (0, 0) };
    assert pair[0] + pair[1] == 42;
}

Structured result selection is deterministic. Children are scanned from left to right on each poll; if multiple outcomes are visible in the same scan, the lower child slot wins.

Optimized state-machine lowering and fallback coroutine lowering must preserve results, cancellation, and exactly-once cleanup semantics. They may differ in allocation shape, poll count, and the tick that performs cleanup.

Groups

future.Group<T, E> starts each child when group.add(future) is called inside the group scope. The error set E must include future.AsyncError.

import std.future;

async fn fetch_a() -> i32 {
    return 10;
}

async fn fetch_b() -> i32 {
    return 32;
}

async fn main() {
    with future.Group<i32, future.AsyncError>() as group {
        group.add(fetch_a());
        group.add(fetch_b());

        let first = try await group.next();
        let second = try await group.next();

        assert first != null;
        assert second != null;
    }
}

group.next() returns a scoped future.Task<T? throws(E)>. It must be awaited inside the group block and cannot be returned, stored outside the block, detached, or passed to ordinary APIs. Use group.cancel_all() to request cancellation for unfinished children.

API Quick Reference

ItemShape
future.readyready<T>(value: T) -> Future<T>
future.sleepsleep(duration: time.Duration) -> Future<void>
future.yieldyield() -> Future<void>
future.readablereadable(fd: usize) -> Future<void>
future.writablewritable(fd: usize) -> Future<void>
future.allall<T...>(futures: Future<T>...) -> Future<R throws(E)>
future.racerace<T...>(futures: Future<T>...) -> Future<T throws(E)>
future.timeouttimeout<T>(future: Future<T throws(E)>, duration: time.Duration) -> Future<T throws(E | future.AsyncError)>
Group.addadd(future: Future<T>)
Group.nextnext() -> Task<T? throws(E)>
Group.cancel_allcancel_all()