Unlike switch, select can only have one expression per case which may force code duplication or a separated function. This proposal is to add the option of multiple select case expressions.
select {
case <-cancel, <-w.(http.CloseNotifier).CloseNotify():
// cancel action
}
Instead of:
select {
case <-cancel:
// cancel action
case <-w.(http.CloseNotifier).CloseNotify():
// cancel action
}
An example with assignment:
cancel := make(chan struct{})
cancelWork := make(chan int)
concurrentA(cancel)
concurrentB(cancelWork)
select {
case <-cancel, k := <-cancelWork:
// unused variable assignments are set to zero value
if k != 0 {
// handle work
}
// cancel action
}
In more detail, the proposal is that multiple expressions for a select case is equivalent to individually cased expressions with a duplicated code block, plus the requirement of all assignments used in the single code block.
My use case is a web page where a browser close and a cancel button press cause the same cancel to occur in a concurrent request. In my case there are only two lines of code duplicated:
// https://github.com/pciet/wichess/blob/master/web_competitive15.go#L112
select {
case <-rdy:
return // the client redirects to a GET /competitive15
case <-cancel:
competitive15Matcher.Cancel(name)
http.NotFound(w, r) // the client ignores the POST response
return
case <-w.(http.CloseNotifier).CloseNotify():
competitive15Matcher.Cancel(name)
http.NotFound(w, r)
return
}
The cases are different (CloseNotify doesn't need the http.NotFound) but the idea remains. Originally posted to golang-nuts: https://groups.google.com/forum/#!topic/golang-nuts/ROxbuskAglc
If you have case x := <-c1, y := <-c2, what happens?
Both variables would have to be used in the case block:
select {
case x := <-c1, y := <-c2:
if x != false {
// x non-zero value received
fmt.Println(x)
break
}
// y non-zero value received
fmt.Println(y)
}
Comma ok is another case too:
select {
case x, ok := <-c1, y, ok := <-c2:
// doesn't compile because of repeated ok var?
select {
case x, okX := <-c1, y, okY := <-c2:
Maybe since ok is always a bool it can have the same name:
select {
// still has a lot of commas
case var1, ok := <-c1, var2, ok := <-c2, var3, ok := <-c3:
if ok == false {
return
}
if var1 != 0 {
fmt.Println(var1)
break
}
if var2 != nil {
fmt.Println(var2)
break
}
fmt.Println(var3)
case <-c4, <-c5, <-c6:
case <-done:
return
}
There's the problem of the zero-value being sent on the chan meaning the block can't figure out which var to use.
Your example use case could be written without duplication as:
select {
case <-rdy:
return // the client redirects to a GET /competitive15
case <-cancel:
case <-w.(http.CloseNotifier).CloseNotify():
}
competitive15Matcher.Cancel(name)
http.NotFound(w, r) // the client ignores the POST response
return
That makes sense. I don't have a good example use case.
@mvdan despite the Go 2 tag do you think this could be a Go 1 feature? It doesn't seem like a breaking change.
There will be no more significant changes to the language before Go 2.0, so I would say very unlikely.
Our use case is similar to @pciet's example.
We have a event or tick case, both trigger the same behaviour.
We ended up moving that into a function. Felt bad to allocate resources on every function call.
I'll try @neild's solution and report if we land into some other problem.
A hypothetical case which wouldn't work this way:
eventAchan and ticker trigger one behaviour.
eventBchan, a closed channel and timeout trigger another behaviour.
fallthrough for switch is something I wasn't aware of. Adding this keyword to select could have the same effect. Here's an old golang-nuts thread about that: https://groups.google.com/forum/#!topic/golang-nuts/j0RobXhmGac
When used with sends on a channel, there is no way to tell which send succeeded in a case. When used with receives that happen to receive the zero value, there is no way to tell which receive succeeded in a case (particularly important since receives from a closed channel get the zero value). The same functionality can also be easily obtained with our current select by factoring out the case bodies.
Most helpful comment
When used with sends on a channel, there is no way to tell which send succeeded in a case. When used with receives that happen to receive the zero value, there is no way to tell which receive succeeded in a case (particularly important since receives from a closed channel get the zero value). The same functionality can also be easily obtained with our current select by factoring out the case bodies.