Directional channels

May 17, 2023

Channel types

The optional <- operator specifies the channel direction, send or receive. If a direction is given, the channel is directional, otherwise it is bidirectional. A channel may be constrained only to send or only to receive by assignment or explicit conversion.

chan T          // can be used to send and receive values of type T
chan<- float64  // can only be used to send float64s
<-chan int      // can only be used to receive ints

When you create a new channel, you get a bidirectional channel.

It is good practice, however, to only expose either a send or receive channel where needed. For example, if you have a function that returns a channel to be read from, you should only return a receive channe:

func ReadAllTheThings() <-chan Thing { // The return value can only receive Things

If you were to use a bidirectional channel here, you would be permitting callers of your function to send things to the channel (to be read by any other potential callers).

In much the same way that assignment to interfaces is implicit, assignment to directional channels is the same. For example, our ReadAllTheThings function internals might look like this:

func ReadAllTheThings() <-chan Thing {
  thingChan := make(chan Thing) // thingChan is bidirectional
  go func(ch chan<- Thing) {
    for _, thing := range fetchThingsFromDatabase() {
      ch <- thing // pass things to the channel
  }(thingChan) // We pass the bidirectional variable, but
               // it is automatically down-converted to
               // the send-only type of `chan<- Thing`

  // We're returning the bidirectional variable, but it
  // is automatically down-converted to receive-only type
  // of `<-chan Thing`
  return thingChan

Quotes from The Go Programming Language Specification Version of December 15, 2022

Share this