Channels โ Pipelines Between Goroutines
A concept Ruby doesn't have. Safe data passing between goroutines.
In Ruby, you pass data between threads with Queue: queue = Queue.new; Thread.new { queue.push(42) }; val = queue.pop.
Go channels are similar but built into the language syntax:
ch := make(chan int)
go func() { ch <- 42 }() // send
val := <-ch // receive
The <- operator is key. ch <- 42 sends to channel, <-ch receives from channel. Same role as Queue#push/pop but more concise.
Buffered vs Unbuffered
make(chan int) โ unbuffered. Sender blocks until receiver is ready. Synchronous handoff.
make(chan int, 10) โ buffer of 10. Sender doesn't block until buffer is full. Like Ruby's SizedQueue.new(10).
select โ Wait on Multiple Channels
select {
case msg := <-ch1:
fmt.Println(msg)
case msg := <-ch2:
fmt.Println(msg)
case <-time.After(5 * time.Second):
fmt.Println("timeout")
}
Handles whichever channel gets a value first. Ruby has nothing like this. IO.select does something similar for sockets, but general-purpose channel multiplexing is Go's domain.
Pipeline Pattern
Chaining channels creates Unix-pipe-like data processing pipelines:
Generate โ Filter โ Transform โ Output
Each stage runs as a concurrent goroutine. Like Ruby's array.select.map.each chaining, but Go chains channels for stream processing.
Ruby to Go
Ruby: Queue.new / push / pop โ Go: make(chan T) / ch <- val / <-ch
Ruby: SizedQueue.new(n) โ Go: make(chan T, n) (buffered channel)
Ruby: N/A โ Go: select { case } (wait on multiple channels)
Ruby: .select.map.each chaining โ Go: channel pipeline
Pros
- ✓ Language-level support โ concurrency patterns without external libraries
- ✓ select expresses timeout, cancellation, multiplexing concisely
Cons
- ✗ Wrong channel direction causes deadlock โ debugging is tricky
- ✗ Pipeline code is verbose compared to Ruby's Enumerable chaining