86

I was reading some go code and say a few different ways to pass go channels. Maybe they are the same but I was wondering if there is any difference since I couldn't find documentation online:

1)

func serve(ch <-chan interface{}){ //do stuff }

2)

func serve(ch chan<- interface{}){ //do stuff }

3)

func serve(ch chan interface{}){ //do stuff }

4)

func server(ch *chan interface{}){ //do stuff}

I was wondering what the difference between them were and if they were just equivalent ways to do the same thing: pass a channel around different goroutines.

NOTE: I am aware that there is no reason to pass a pointer to a chan, map, or slice, or function value, since those are all reference types which internally contain a pointer (the exception would be if you want the callee to change the reference type header). The only reason I provided it is for completeness (i.e. to really provide every way a channel could be attempted to be passed as a parameter and to make on question that hopefully, references all ways to do this and compares them).

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Charlie Parker
  • 5,884
  • 57
  • 198
  • 323

4 Answers4

99

I always recommend that the direction is passed everywhere it is possible, e.g.

func serve(ch <-chan SomeType) { /*do stuff*/ }

func serve(ch chan<- SomeType) { /*do stuff*/ }

By including the arrow <-chan or chan<-, you are achieving three things:

  • You are making it clear that the parameter is one end of a channel.
  • You are expressing clearly which end is being supplied. chan<- is the sending (writing) end. <-chan is the receiving (reading) end.
  • You are giving more information to the compiler for checking. If the function body tries to use the wrong end of the channel, the compiler can raise an error.

These are good reasons to show the channel end whenever possible.

Your third case depicts not specifying the end of the channel. This allows both ends of the channel to be accessed, which will be correct in some cases but in other cases may lead to accidental errors.

The fourth case, passing a pointer to a channel, is quite unusual and perhaps a bit odd. If you wanted to change the channel, it would be clearer to include it as a return parameter instead.

Rick-777
  • 9,714
  • 5
  • 34
  • 50
  • 9
    Nice answer except that the correct term for "end" is "direction" / "channel direction". Please see [here in the spec](https://golang.org/ref/spec#Channel_types). – Siu Ching Pong -Asuka Kenji- Dec 28 '15 at 03:45
  • 4
    The spec does indeed say 'direction', but that's not necessarily helpful for newbies. With CSP designs, one can think loosely of channels like pipes for fluid - pour it in one end and out at the other. In Occam, this distinction is explicit, but in Go it is (deliberately) more blurred. In particular, with Go it is possible to use a buffered channel completely within a *single goroutine*, so the spec refers to direction rather than ends. This kind of usage is *not* possible in CSP, but Go's channel's implementation is flexible and allows it. – Rick-777 Dec 29 '15 at 21:32
  • In addition, it may be helpful to think of channel ends when considering whether a network can deadlock. There are theoretical proofs that a directed graph is deadlock free if it is acyclic. You need to think about the direction of the channels to reason about this. The *ends* and the *direction* are in effect interchangeable - both are useful concepts. – Rick-777 Dec 29 '15 at 21:33
  • As a Go newb, I found calling it an *end* of channel confusing, since that sounds like it would be referring to the receiving/outgoing "end", exclusively. I think it would be clearer if you said "start or end of a channel." Also, specifying which one refers to the start and which refers to the end would be helpful for those who have no idea what those arrows mean. [smishra explains the arrows well in their answer.](https://stackoverflow.com/a/35759603/879628) – apokaliptis Oct 14 '21 at 17:02
  • I see your point. It's *end* as in *end of a hosepipe*. I could add more words but might introduce different ambiguities. – Rick-777 Oct 18 '21 at 13:40
  • I changed 'an end' to 'one end'. Hope that's clearer without being more wordy. – Rick-777 Oct 18 '21 at 13:41
50

The rule of thumb: Arrow shows if the data is going into (output) or going out of (input) channel. No arrow is general purpose channel.

chan <-          writing to channel (output channel)
<- chan          reading from channel (input channel)
chan             read from or write to channel (input/output channel)
smishra
  • 3,122
  • 29
  • 31
42

These are different types of channels. See http://golang.org/ref/spec#Channel_types . For the pointer stuff: Uncommon, but might be useful if you want to change the channel from inside the function (never saw that in the wild).

Volker
  • 40,468
  • 7
  • 81
  • 87
3

From the docs: channel_types

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

Example:

// can be used to send and receive
func Bidirectional(c chan any) {
    receive := <-c
    fmt.Println(receive)
    c <- "sending or writing..."
}

// can only be used to receive
func ReceiveOnly(c <-chan any) {
    receive := <-c
    fmt.Println(receive)
    // c <- "" // error. since it only be used to receive not send
}

// can only be used to send of type "any"
func SendOnly(c chan<- any) {
    // receive := <-c       // error. since it only be used to send not receive
    c <- "Msg..."
}

go-playground

Carson
  • 6,105
  • 2
  • 37
  • 45