What is the difference between switch and select in Go?
Is there any difference between switch
and select
in Go,
apart from the fact that one takes an argument and the other not?
A select
is only used with channels. Example
A switch
is used with concrete types. Example
A select
will choose multiple valid options at random, while aswitch
will go in sequence (and would require a fallthrough to match multiple.)
Note that a switch can also go over types for interfaces when used with the keyword .(type)
var a interface{}
a = 5
switch a.(type) {
case int:
fmt.Println("an int.")
case int32:
fmt.Println("an int32.")
}
// in this case it will print "an int."
switch is used to make a decision based on a variable value of any type. Read this for more details:
Go's switch is more general than C's. The expressions need not be constants or even integers, the cases are evaluated top to bottom until a match is found, and if the switch has no expression it switches on true. It's therefore possible—and idiomatic—to write an if-else-if-else chain as a switch.
Sample Use: (Go Playground)
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.", os)
}
}
The select statement lets a goroutine wait on multiple communication operations.
A select blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready. Here is one example: (Go Playground)
package main
import (
"fmt"
"time"
)
func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(" .")
time.Sleep(50 * time.Millisecond)
}
}
}
Select statements
A "select" statement chooses which of a set of possible send or receive operations will proceed. It looks similar to a "switch" statement but with the cases all referring to communication operations.
Switch statements
"Switch" statements provide multi-way execution. An expression or type specifier is compared to the "cases" inside the "switch" to determine which branch to execute. There are two forms: expression switches and type switches. In an expression switch, the cases contain expressions that are compared against the value of the switch expression. In a type switch, the cases contain types that are compared against the type of a specially annotated switch expression. The switch expression is evaluated exactly once in a switch statement.
Yes, there are many differences:
-
select
works just on channel events (receive, close or wait), but you can useswitch
just for comparing channel data likecase <-ch == 1:
-
switch
works in deterministic way like multipleif
orif else
statement, butselect
chooses thecase
in non-deterministic way: you can't say which case runs first inselect
- you can't use
fallthrough
inselect
- in
switch
An expression or type specifier is compared to thecases
inside theswitch
to determine which branch to execute. -
switch
is not blocking itself, butselect
is blocking underlying goroutine unless you usedefault
-
switch
has two forms: expression switches and type switches - in blocking
select
(withoutdefault
) there is no CPU usage (goroutine sleep) - Unlike
select
you can't usecase <-ch:
insideswitch
.
Working sample Code:
package main
import "fmt"
func main() {
ch := make(chan int, 4)
ch <- 1
ch <- 2
ch <- 3
ch <- 4
close(ch)
switch {
//case <-ch: // invalid case <-ch in switch (mismatched types int and bool)
case <-ch == 1:
fmt.Println("switch1")
fallthrough
case <-ch == 2:
fmt.Println("switch2")
}
select {
case d := <-ch:
fmt.Println("select1 d=", d)
case d := <-ch:
fmt.Println("select2 d=", d)
}
}
output:
switch1
switch2
select2 d= 2
output of another run:
switch1
switch2
select1 d= 2