Golang channel in a struct: Can it be directional when passed to a function?

Bart Silverstrim picture Bart Silverstrim · Jun 2, 2017 · Viewed 19.8k times · Source

When passing a channel to a function, I know you can specify the direction that channel may use the channel for; for example,

func MyFunc(ch chan<- string) {
    ch <- "Hello"
}

"ch" can only be used by MyFunc to send strings to a receiver elsewhere and MyFunc cannot listen for messages from ch.

To simplify creating a dynamic number of channels to some number of goroutines, I created a struct containing channels.

type ChanStruct struct {
    chMessages chan string
}

Then I instantiate a structs:

var slcChanStruct []ChanStruct
for a:= 0; a <=2; a++ {
    var tmpChanStruct ChanStruct
    tmpChanStruct.chMessages = make(chan string)
    slcChanStruct = append(slcChanStruct, tmpChanStruct)
}

Now I have 3 structs I can individually read/write with a channel by ranging over a slice of structs. But when I send them off to goroutines:

for a:=0; a <=2; a++{
    go SomeFunc(slcChanStruct[a])
}

...is there a way to add a bit of safety by specifying that the goroutines can only, for example, send using the ChanStruct.ch channel?

NOTE the reason I was doing this is that if I don't know how many channels I'll need for a parallel set of processes (the number of processes are passed as a command line argument) using a struct with channels means I can create a slice that can be passed to any number of goroutines and directly access them individually by ranging over them; before, I had to create a set number of channels if I wanted to individually read from a number of goroutines. I'd like to add some safety to this by specifying that processes can only use a channel a certain way, like I could when using individual channels (individual channels mean I can do something like telling a particular goroutine to quit without other goroutines intercepting it). Is there a more idiomatic way to do this if I can't add directionality to functions using the channel in a struct?

Answer

abhink picture abhink · Jun 2, 2017

On top of @Adrian's suggestions, if you have to ensure one way communication on a channel, you can always use an anonymous function:

for a:=0; a <=2; a++{
  go func f(ch <-string) {
    SomeFunc(ch)
  }(slcChanStruct[a].chMessages)
}

Note that you'll have to pass the channel to SomeFunc instead of the struct.

If you still want to perform both way communication on your channel, you can re assign the channel to a one directional type:

type ChanStruct struct {
    chMessages chan string
}

type ChanStructRecv struct {
    chMessages <-chan string
}

// Update SomeFunc type:

func SomeFunc(s ChanStructRecv) {
  // ...
}

Finally, the loop can be modified similarly:

for a:=0; a <=2; a++{
  go func f(s ChanStruct) {
    var sr ChanStructRecv
    sr.chMessages = s.chMessages
    SomeFunc(sr)
  }(slcChanStruct[a])
}