What is the difference between switch and select in Go?

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 use switch just for comparing channel data like case <-ch == 1:
  • switch works in deterministic way like multiple if or if else statement, but select chooses the case in non-deterministic way: you can't say which case runs first in select
  • you can't use fallthrough in select
  • in switch An expression or type specifier is compared to the cases inside the switch to determine which branch to execute.
  • switch is not blocking itself, but select is blocking underlying goroutine unless you use default
  • switch has two forms: expression switches and type switches
  • in blocking select (without default) there is no CPU usage (goroutine sleep)
  • Unlike select you can't use case <-ch: inside switch.

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

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)
        }
    }
}

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."