Go

Go has no unbounded channels. Why not, and how would you build one?

Question 219HardGo 1.22 to 1.25

A channel's capacity is fixed at make time. The Go team has declined to add unbounded channels (the proposal is golang/go#20352) because a bounded buffer gives you backpressure: a slow consumer eventually blocks the producer. An unbounded queue hides that until memory runs out. If you really need one, for example to keep an event loop from ever blocking, build it from a goroutine, a slice, and the nil-channel trick:

func Unbounded[T any](ctx context.Context, in <-chan T) <-chan T {
	out := make(chan T)
	go func() {
		defer close(out)
		var queue []T
		for in != nil || len(queue) > 0 {
			var sendCh chan T // nil: send case disabled
			var next T
			if len(queue) > 0 {
				sendCh, next = out, queue[0]
			}
			select {
			case v, ok := <-in:
				if !ok {
					in = nil // stop receiving, flush the queue
					continue
				}
				queue = append(queue, v)
			case sendCh <- next:
				var zero T
				queue[0] = zero // let the GC reclaim the value
				queue = queue[1:]
			case <-ctx.Done():
				return
			}
		}
	}()
	return out
}

Points to mention: next is computed before the select because send values are evaluated even for disabled cases. Zeroing queue[0] avoids keeping pointers alive in the slice's backing array. The ctx case keeps the goroutine from leaking if the consumer walks away. Order is FIFO. Memory growth is unbounded by design, so expose a length metric or put a hard cap on it and drop items.

More on Channels & select

All 38 Channels & select questions