std.channel
Explicit imports for bounded channels and timed waits and selection. Supported target: x86_64 Linux GNU. Imports are allocation-free aliases of the existing prelude facilities, with identical generic type identity.
| Export | Contract |
|---|---|
channel[T](capacity: Positive[i64]) | Result[tuple[Sender[T], Receiver[T]], AllocError] |
Sender[T], Receiver[T] | Owned endpoints; explicit share() retains the queue |
select_recv(&first, &second) | Result[Selected[A, B], SelectError] |
select_recv_nowait(&first, &second) | Same, without waiting for messages |
select_recv_timeout(&first, &second, timeout_ms: u64) | Same, with a millisecond budget |
SendError[T] | Full(value), Disconnected(value) |
RecvError | Empty, Disconnected |
SendTimeoutError[T] | TimedOut(value), Disconnected(value) |
RecvTimeoutError | TimedOut, Disconnected |
Selected[A, B] | First(value), Second(value) |
SelectError | Empty, TimedOut, Disconnected |
A worker sums messages until the sender’s scope drops it:
from std.channel import channel, Receiver, SendError
from std.thread import spawn, SpawnError
def total(inbox: Receiver[i64]) -> Result[i64, OverflowError[i64]]:
mut sum = 0
while True:
match inbox.recv():
case Ok(value):
sum += value
case Err(_):
break
Ok(sum)
def main() -> Result[(), AllocError | SendError[i64] | SpawnError[()] | IoError | OverflowError[i64]]:
sender, inbox = channel[i64](2)?
with spawn(total, inbox).drop_job()? as task:
with sender as outbox:
for n in range(1, 6):
outbox.send(n * 10)?
print(task.join()?)?
Ok(())
150
The linked facility pages specify every endpoint method. Capacity must be
a Positive[i64] and bounds queued messages. Computed capacities use
Positive.try_from(count)?; invalid literals are compile errors. Construction
returns only AllocError for capacity overflow or exhaustion. Construction makes one fallible allocation;
send, receive, share, error transport and selection allocate nothing. Messages
move without deep copying; rejected sends return the original value. Queue
operations have constant bookkeeping plus value transfer, with unbounded lock
and scheduling latency; destroying queued values costs their cleanup. Last
receiver drop destroys queued values and wakes blocked senders; last sender drop
allows buffered messages to drain before disconnection.
Messages must be wholly owned and worker-eligible. References, uncertified
foreign handles, and recursive receiver retention are still rejected. Imports
do not weaken the checks for live opposite endpoints across joins or future
waits. Timeouts are budgets, not hard deadlines. Use
std.time to compute remaining milliseconds; tokens do not interrupt
an ordinary channel wait. No unbounded channel or arbitrary selector is added.
Acknowledgements
- Contracts follow Rust’s
sync_channeland crossbeam-channel; no Crossbeam code is used.