Thread Spawn

This section describes the @spawn intrinsic and the JoinHandle(R) prelude type (ADR-0084).

@spawn(fn, arg)

spawn = "@spawn" "(" fn_ref "," argument ")" ;
fn_ref = IDENT ;

@spawn(fn, arg) runs fn on a new thread with arg as its sole parameter and returns a linear JoinHandle(R) where R is fn's return type. The handle MUST be consumed via join(self) -> R (linearity, ADR-0067).

The first argument MUST be a top-level function name. Methods, anonymous functions, and comptime-bound function values are not permitted.

The named function MUST take exactly one parameter. Multi-input workers wrap their inputs in a tuple or struct on the caller's side.

The argument's type MUST match the function's parameter type under the regular bidirectional unification rules.

The function's parameter type MUST be classified at least Send on the trichotomy (ADR-0084 §3.15) — a structurally Unsend parameter makes the spawn unsafe and is rejected at compile time. The @mark(checked_send) and @mark(checked_sync) overrides on the parameter's type lift the structural classification.

The function's parameter type MUST NOT be Linear. Linear values are per-thread today; future work will lift this restriction.

The function's parameter type MUST NOT be a Ref(T) or MutRef(T). References are scope-bound (ADR-0076), so the spawned thread cannot outlive the caller's stack frame.

The function's return type MUST be classified at least Send. The join site transfers the result back across the thread boundary, which would be unsafe for an Unsend return.

A panic in the spawned function aborts the whole process. Future work may add a Result-typed join.

fn worker(input: i32) -> i32 {
    input * 2
}

fn main() -> i32 {
    let h = @spawn(worker, 21);
    h.join()  // 42
}

JoinHandle(R)

JoinHandle(R) is a prelude-resident parameterized type returned by @spawn. It carries the bookkeeping for one spawned thread.

The JoinHandle(R) posture is Linear. The handle MUST be consumed via join(self) -> R; dropping it without consumption is a compile-time error.

The JoinHandle(R) thread-safety classification is Send, unconditionally. The struct stores an opaque thread-handle pointer (not the R value), and the R value is checked to be at least Send at the @spawn site, so JoinHandle(R) is always paired with a Send R by construction. The handle itself MAY be moved to another thread to join from there.

JoinHandle::join(self) -> R consumes the handle and blocks until the spawned function returns, yielding the result.

fn worker(input: i32) -> i32 { input + 1 }

fn main() -> i32 {
    let h: JoinHandle(i32) = @spawn(worker, 41);
    let result: i32 = h.join();
    result  // 42
}