Canais
Nesta página
Canais são a forma idiomática de comunicar tarefas em Zolo. channel(N) cria
um canal com buffer de capacidade N. ch.send(v) enfileira um valor;
ch.recv() retira e bloqueia o chamador enquanto o canal estiver vazio —
por isso consumidores sempre precisam rodar dentro de spawn { ... }.
recv retorna um Option: use .is_some() / .unwrap() para inspecionar, ou
.unwrap_or(padrão) para fornecer um valor de fallback. Quando o canal fecha e
o buffer está vazio, recv devolve nil (opção vazia).
Canal de rendezvous (channel(0)) com dois spawns: produtor fecha, consumidor verifica is_some e is_none.
// Feature: channels — typed CSP-style message passing
// Syntax: `channel(buffer_size)` creates one. `ch.send(v)` enqueues,
// `ch.recv()` dequeues. `recv` returns an `Option` — use `is_some` /
// `unwrap` (or `for x in ch { ... }` which strips Option for you).
// `recv` blocks the caller, so it must run inside a coroutine.
// When to use: producer/consumer pipelines, bounded queues, fan-out
// of work across spawned tasks.
let ch = channel(0)
scope {
spawn {
ch.send(10)
ch.send(20)
ch.close()
}
spawn {
let a = ch.recv()
let b = ch.recv()
let c = ch.recv()
print(a.is_some()) // expected: true
print(a.unwrap()) // expected: 10
print(b.is_some()) // expected: true
print(b.unwrap()) // expected: 20
print(c.is_none()) // expected: true (channel closed, drained)
}
}
O padrão for x in ch { ... } drena o canal até ele fechar, desembrulhando o
Option automaticamente. É a forma mais limpa de consumir um stream de
mensagens.
Produtor envia três valores e fecha; consumidor usa for x in ch e imprime cada um.
// Feature: receive loop — `for x in ch` until close
// Syntax: `for x in ch { ... }` calls recv internally and exits when
// the channel is closed. The body runs once per received value.
// When to use: streaming consumers, log readers, event drainers —
// anywhere "process every message until the producer stops".
let ch = channel(0)
scope {
spawn {
ch.send(1)
ch.send(2)
ch.send(3)
ch.close()
}
spawn {
for x in ch {
print(x)
}
}
}
print("done")
// expected:
// 1
// 2
// 3
// done
channel(N) com N >= 1 bufferiza até N valores antes de aplicar
contrapressão: o produtor bloqueia no send enquanto o buffer estiver
cheio. Isso nivela rajadas sem crescimento ilimitado de memória.
Buffer de tamanho 2: o terceiro send espera até o consumidor liberar um slot.
// Feature: bounded channels — backpressure built in
// Syntax: `channel(N)` reserves a buffer of size N. Sends past
// capacity yield until the consumer drains a slot.
// When to use: rate-limit producers, prevent memory blowups in
// pipelines, smooth bursty workloads with a fixed-size queue.
let ch = channel(2)
scope {
spawn {
ch.send("a")
ch.send("b")
// The buffer is full; this third send waits until the consumer
// recvs at least once.
ch.send("c")
ch.close()
}
spawn {
print(ch.recv().unwrap()) // expected: a
print(ch.recv().unwrap()) // expected: b
print(ch.recv().unwrap()) // expected: c
print(ch.recv().is_none()) // expected: true
}
}
Desafio
Modifique o exemplo básico para usar channel(4) em vez de channel(0) e
observe como o produtor avança sem esperar o consumidor.