Control flow with switch statements
Like other programming languages, Go supports switch
statements. You use switch
statements to avoid chaining multiple if
statements. By using switch
statements, you avoid the difficulty of maintaining and reading code that includes many if
statements. These statements also make complicated conditions easier to construct. Take a look at switch
statements in the following sections.
Basic switch syntax
Like the if
statement, the switch
condition doesn't require parentheses. In its simplest form, a switch
statement looks like this:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
sec := time.Now().Unix()
rand.Seed(sec)
i := rand.Int31n(10)
switch i {
case 0:
fmt.Print("zero...")
case 1:
fmt.Print("one...")
case 2:
fmt.Print("two...")
}
fmt.Println("ok")
}
If you run the preceding code several times, you'll see a different output every time. (But if you run the code in the Go Playground, you'll get the same result every time. That's one of the service's limitations.)
Go compares each case of the switch
statement until it finds a match for the condition. But notice that the previous code doesn't cover all possible cases of num
variable values. If num
ends up being 5
, the program output is ok
.
You can also be more specific about the default use case and include it like this:
switch i {
case 0:
fmt.Print("zero...")
case 1:
fmt.Print("one...")
case 2:
fmt.Print("two...")
default:
fmt.Print("no match...")
}
Notice that for the default
case, you don't write a validating expression. The value of the i
variable is validated against the case
statements, and the default
case handles any unvalidated values.
Use multiple expressions
Occasionally, more than one expression matches only one case
statement. In Go, if you want a case
statement to include more than one expression, separate the expressions by using commas (,
). This technique allows you to avoid duplicate code.
The following code sample shows how to include multiple expressions.
package main
import "fmt"
func location(city string) (string, string) {
var region string
var continent string
switch city {
case "Delhi", "Hyderabad", "Mumbai", "Chennai", "Kochi":
region, continent = "India", "Asia"
case "Lafayette", "Louisville", "Boulder":
region, continent = "Colorado", "USA"
case "Irvine", "Los Angeles", "San Diego":
region, continent = "California", "USA"
default:
region, continent = "Unknown", "Unknown"
}
return region, continent
}
func main() {
region, continent := location("Irvine")
fmt.Printf("John works in %s, %s\n", region, continent)
}
Notice that the values you include in the expressions for the case
statement correspond to the data type of the variable that the switch
statement validates. If you include an integer value as a new case
statement, the program won't compile.
Invoke a function
A switch
can also invoke a function. From that function, you can write case
statements for possible return values. For example, the following code calls the time.Now()
function. The output that it prints depends on the current weekday.
package main
import (
"fmt"
"time"
)
func main() {
switch time.Now().Weekday().String() {
case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
fmt.Println("It's time to learn some Go.")
default:
fmt.Println("It's the weekend, time to rest!")
}
fmt.Println(time.Now().Weekday().String())
}
When you call a function from a switch
statement, you can modify its logic without changing the expression because you always validate what the function returns.
Also, you can call a function from a case
statement. Use this technique, for instance, to match a particular pattern by using a regular expression. Here's an example:
package main
import "fmt"
import "regexp"
func main() {
var email = regexp.MustCompile(`^[^@]+@[^@.]+\.[^@.]+`)
var phone = regexp.MustCompile(`^[(]?[0-9][0-9][0-9][). \-]*[0-9][0-9][0-9][.\-]?[0-9][0-9][0-9][0-9]`)
contact := "foo@bar.com"
switch {
case email.MatchString(contact):
fmt.Println(contact, "is an email")
case phone.MatchString(contact):
fmt.Println(contact, "is a phone number")
default:
fmt.Println(contact, "is not recognized")
}
}
Notice that the switch
block has no validating expression. Let's talk about that concept in the next section.
Omit a condition
In Go, you can omit a condition in a switch
statement like you do in an if
statement. This pattern is like comparing a true
value as if you were forcing the switch
statement to run all the time.
Here's an example of how to write a switch
statement without a condition:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().Unix())
r := rand.Float64()
switch {
case r > 0.1:
fmt.Println("Common case, 90% of the time")
default:
fmt.Println("10% of the time")
}
}
The program always runs this type of switch
statement because the condition is always true. A conditional switch
block can be easier to maintain than a long chain of if
and else if
statements.
Make the logic fall through to the next case
In some programming languages, you write a break
keyword at the end of every case
statement. But in Go, when the logic falls into one case, it exits the switch
block unless you explicitly stop it. To make the logic fall through to the next immediate case, use the fallthrough
keyword.
To understand this pattern better, look at the following code sample.
package main
import (
"fmt"
)
func main() {
switch num := 15; {
case num < 50:
fmt.Printf("%d is less than 50\n", num)
fallthrough
case num > 100:
fmt.Printf("%d is greater than 100\n", num)
fallthrough
case num < 200:
fmt.Printf("%d is less than 200", num)
}
}
Run the code and analyze the output:
15 is less than 50
15 is greater than 100
15 is less than 200
Do you see anything wrong?
Notice that because num
is 15 (less than 50), it matches the first case. But num
isn't greater than 100. And because the first case
statement has a fallthrough
keyword, the logic goes to the next case
statement immediately without validating the case. So you have to be careful when you use the fallthrough
keyword. You might not want the behavior that this code creates.