Skip to main content

Go Switch Statements

Switch statements in Go are powerful and flexible constructs that provide an elegant way to handle multiple conditions and make your code more readable and maintainable. Unlike many other programming languages, Go's switch statements have unique characteristics that make them particularly useful for various programming scenarios. This comprehensive guide will teach you everything you need to know about Go's switch statements, from basic expression switches to advanced type switches and complex pattern matching.

Understanding Switch Statements in Go

What Are Switch Statements?

Switch statements allow you to compare a value against multiple possible cases and execute different code blocks based on the match. They provide a cleaner and more readable alternative to long chains of if/else statements, especially when dealing with multiple discrete values.

Go's Unique Approach to Switch Statements

Go's switch statements have several distinctive features that set them apart from other programming languages:

Expression Switches

Go allows you to switch on expressions, not just values, making switch statements more flexible and powerful.

Type Switches

Go provides special type switches that allow you to switch based on the type of an interface value, enabling dynamic type checking and conversion.

Automatic Break Behavior

Unlike languages like C, Java, or JavaScript, Go's switch statements don't fall through to the next case by default, eliminating the need for explicit break statements.

Optional Expression

Go allows you to omit the switch expression, creating a cleaner alternative to if/else chains.

Fallthrough Control

Go provides explicit fallthrough statements when you need the traditional fall-through behavior.

Expression Switches

Basic Expression Switch

The most common type of switch statement in Go is the expression switch, which compares a value against multiple cases.

package main

import "fmt"

func main() {
// Basic expression switch
day := "Monday"

switch day {
case "Monday":
fmt.Println("Start of the work week")
case "Tuesday":
fmt.Println("Second day of work")
case "Wednesday":
fmt.Println("Mid-week")
case "Thursday":
fmt.Println("Almost Friday")
case "Friday":
fmt.Println("TGIF!")
case "Saturday", "Sunday":
fmt.Println("Weekend!")
default:
fmt.Println("Unknown day")
}

// Switch with integer values
score := 85

switch score {
case 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100:
fmt.Println("Grade: A")
case 80, 81, 82, 83, 84, 85, 86, 87, 88, 89:
fmt.Println("Grade: B")
case 70, 71, 72, 73, 74, 75, 76, 77, 78, 79:
fmt.Println("Grade: C")
case 60, 61, 62, 63, 64, 65, 66, 67, 68, 69:
fmt.Println("Grade: D")
default:
fmt.Println("Grade: F")
}

// Switch with character values
grade := 'B'

switch grade {
case 'A', 'a':
fmt.Println("Excellent!")
case 'B', 'b':
fmt.Println("Good job!")
case 'C', 'c':
fmt.Println("Satisfactory")
case 'D', 'd':
fmt.Println("Needs improvement")
case 'F', 'f':
fmt.Println("Failed")
default:
fmt.Println("Invalid grade")
}
}

Switch with Expressions

Go allows you to use expressions in both the switch statement and the case values, making switch statements extremely flexible.

package main

import "fmt"

func main() {
// Switch with expressions
age := 25

switch {
case age < 13:
fmt.Println("Child")
case age < 20:
fmt.Println("Teenager")
case age < 60:
fmt.Println("Adult")
default:
fmt.Println("Senior")
}

// Switch with calculated expressions
score := 85
attendance := 95

switch {
case score >= 90 && attendance >= 90:
fmt.Println("Honor roll student")
case score >= 80 && attendance >= 80:
fmt.Println("Good student")
case score >= 70 && attendance >= 70:
fmt.Println("Average student")
case score >= 60 && attendance >= 60:
fmt.Println("Below average student")
default:
fmt.Println("Needs improvement")
}

// Switch with function calls
temperature := 25
humidity := 60

switch {
case temperature > 30 && humidity > 70:
fmt.Println("Hot and humid - stay indoors!")
case temperature > 30:
fmt.Println("Hot day - stay hydrated!")
case temperature > 20 && humidity < 50:
fmt.Println("Perfect weather for outdoor activities!")
case temperature > 10:
fmt.Println("Pleasant weather")
default:
fmt.Println("Cool weather - dress warmly")
}
}

Switch with Variable Declaration

Go allows you to declare variables in the switch statement, similar to the if statement, which is particularly useful for function calls that return values.

package main

import (
"fmt"
"strconv"
)

func main() {
// Switch with variable declaration
userInput := "42"

switch num, err := strconv.Atoi(userInput); {
case err != nil:
fmt.Printf("Error converting '%s': %v\n", userInput, err)
case num < 0:
fmt.Printf("Negative number: %d\n", num)
case num == 0:
fmt.Println("Zero")
case num > 0 && num <= 10:
fmt.Printf("Small positive number: %d\n", num)
case num > 10 && num <= 100:
fmt.Printf("Medium positive number: %d\n", num)
default:
fmt.Printf("Large positive number: %d\n", num)
}

// Switch with multiple variable declarations
x, y := 10, 20

switch sum := x + y; {
case sum < 20:
fmt.Printf("Small sum: %d\n", sum)
case sum < 50:
fmt.Printf("Medium sum: %d\n", sum)
default:
fmt.Printf("Large sum: %d\n", sum)
}

// Switch with type checking
var value interface{} = 42

switch v := value.(type) {
case int:
fmt.Printf("Integer value: %d\n", v)
case float64:
fmt.Printf("Float value: %.2f\n", v)
case string:
fmt.Printf("String value: %s\n", v)
default:
fmt.Printf("Unknown type: %T\n", v)
}
}

Multiple Case Values

Go allows you to specify multiple values in a single case, separated by commas, making your code more concise and readable.

package main

import "fmt"

func main() {
// Multiple case values
month := "December"

switch month {
case "December", "January", "February":
fmt.Println("Winter season")
case "March", "April", "May":
fmt.Println("Spring season")
case "June", "July", "August":
fmt.Println("Summer season")
case "September", "October", "November":
fmt.Println("Autumn season")
default:
fmt.Println("Unknown month")
}

// Multiple case values with different types
var value interface{} = "hello"

switch value {
case "hello", "hi", "hey":
fmt.Println("Greeting")
case 1, 2, 3, 4, 5:
fmt.Println("Small number")
case true, false:
fmt.Println("Boolean value")
default:
fmt.Println("Other value")
}

// Multiple case values with expressions
score := 85

switch {
case score >= 90:
fmt.Println("Grade: A")
case score >= 80, score >= 70 && score < 80:
fmt.Println("Grade: B or C")
case score >= 60:
fmt.Println("Grade: D")
default:
fmt.Println("Grade: F")
}
}

Fallthrough Behavior

Default No Fallthrough

Unlike many other programming languages, Go's switch statements don't fall through to the next case by default, which eliminates common bugs and makes code more predictable.

package main

import "fmt"

func main() {
// Default no fallthrough behavior
day := "Monday"

switch day {
case "Monday":
fmt.Println("Monday: Start of work week")
// No fallthrough - execution stops here
case "Tuesday":
fmt.Println("Tuesday: Second day")
// No fallthrough - execution stops here
case "Wednesday":
fmt.Println("Wednesday: Mid-week")
// No fallthrough - execution stops here
case "Thursday":
fmt.Println("Thursday: Almost Friday")
// No fallthrough - execution stops here
case "Friday":
fmt.Println("Friday: TGIF!")
// No fallthrough - execution stops here
case "Saturday", "Sunday":
fmt.Println("Weekend!")
// No fallthrough - execution stops here
default:
fmt.Println("Unknown day")
}

// Comparison with if/else chain
if day == "Monday" {
fmt.Println("Monday: Start of work week")
} else if day == "Tuesday" {
fmt.Println("Tuesday: Second day")
} else if day == "Wednesday" {
fmt.Println("Wednesday: Mid-week")
} else if day == "Thursday" {
fmt.Println("Thursday: Almost Friday")
} else if day == "Friday" {
fmt.Println("Friday: TGIF!")
} else if day == "Saturday" || day == "Sunday" {
fmt.Println("Weekend!")
} else {
fmt.Println("Unknown day")
}
}

Explicit Fallthrough

When you need the traditional fall-through behavior, Go provides the fallthrough statement to explicitly continue execution to the next case.

package main

import "fmt"

func main() {
// Explicit fallthrough
score := 85

switch score {
case 100:
fmt.Println("Perfect score!")
fallthrough
case 90, 91, 92, 93, 94, 95, 96, 97, 98, 99:
fmt.Println("Grade: A")
fallthrough
case 80, 81, 82, 83, 84, 85, 86, 87, 88, 89:
fmt.Println("Grade: B")
fallthrough
case 70, 71, 72, 73, 74, 75, 76, 77, 78, 79:
fmt.Println("Grade: C")
// No fallthrough - execution stops here
case 60, 61, 62, 63, 64, 65, 66, 67, 68, 69:
fmt.Println("Grade: D")
default:
fmt.Println("Grade: F")
}

// Fallthrough with conditions
userType := "premium"

switch userType {
case "vip":
fmt.Println("VIP user benefits:")
fallthrough
case "premium":
fmt.Println("Premium user benefits:")
fallthrough
case "standard":
fmt.Println("Standard user benefits:")
fallthrough
default:
fmt.Println("Basic user benefits:")
}

// Conditional fallthrough
value := 5

switch value {
case 1, 2, 3:
fmt.Println("Small number")
if value == 3 {
fallthrough // Only fall through for value 3
}
case 4, 5, 6:
fmt.Println("Medium number")
default:
fmt.Println("Large number")
}
}

Type Switches

Basic Type Switches

Type switches are a special form of switch statement that allows you to switch based on the type of an interface value, enabling dynamic type checking and conversion.

package main

import "fmt"

func main() {
// Basic type switch
var value interface{} = 42

switch v := value.(type) {
case int:
fmt.Printf("Integer: %d\n", v)
case float64:
fmt.Printf("Float: %.2f\n", v)
case string:
fmt.Printf("String: %s\n", v)
case bool:
fmt.Printf("Boolean: %t\n", v)
default:
fmt.Printf("Unknown type: %T\n", v)
}

// Type switch with different values
values := []interface{}{
42,
3.14,
"hello",
true,
[]int{1, 2, 3},
map[string]int{"a": 1},
}

for i, val := range values {
fmt.Printf("Value %d: ", i+1)
switch v := val.(type) {
case int:
fmt.Printf("Integer %d\n", v)
case float64:
fmt.Printf("Float %.2f\n", v)
case string:
fmt.Printf("String '%s'\n", v)
case bool:
fmt.Printf("Boolean %t\n", v)
case []int:
fmt.Printf("Integer slice %v\n", v)
case map[string]int:
fmt.Printf("String-int map %v\n", v)
default:
fmt.Printf("Unknown type %T\n", v)
}
}
}

Type Switches with Custom Types

Type switches work with custom types and can be used to implement polymorphic behavior based on types.

package main

import "fmt"

// Custom types for demonstration
type Shape interface {
Area() float64
}

type Circle struct {
Radius float64
}

func (c Circle) Area() float64 {
return 3.14159 * c.Radius * c.Radius
}

type Rectangle struct {
Width, Height float64
}

func (r Rectangle) Area() float64 {
return r.Width * r.Height
}

type Triangle struct {
Base, Height float64
}

func (t Triangle) Area() float64 {
return 0.5 * t.Base * t.Height
}

func processShape(shape Shape) {
fmt.Printf("Processing shape: ")

switch s := shape.(type) {
case Circle:
fmt.Printf("Circle with radius %.2f, area: %.2f\n", s.Radius, s.Area())
case Rectangle:
fmt.Printf("Rectangle %.2fx%.2f, area: %.2f\n", s.Width, s.Height, s.Area())
case Triangle:
fmt.Printf("Triangle base %.2f, height %.2f, area: %.2f\n", s.Base, s.Height, s.Area())
default:
fmt.Printf("Unknown shape type: %T\n", s)
}
}

func main() {
// Create different shapes
shapes := []Shape{
Circle{Radius: 5.0},
Rectangle{Width: 4.0, Height: 6.0},
Triangle{Base: 3.0, Height: 4.0},
}

// Process each shape
for _, shape := range shapes {
processShape(shape)
}

// Type switch with interface{} values
var values []interface{} = []interface{}{
Circle{Radius: 3.0},
Rectangle{Width: 2.0, Height: 3.0},
"not a shape",
42,
}

fmt.Println("\nProcessing mixed values:")
for i, val := range values {
fmt.Printf("Value %d: ", i+1)
switch v := val.(type) {
case Shape:
fmt.Printf("Shape with area %.2f\n", v.Area())
case string:
fmt.Printf("String: %s\n", v)
case int:
fmt.Printf("Integer: %d\n", v)
default:
fmt.Printf("Unknown type: %T\n", v)
}
}
}

Type Switches with Nil Handling

Type switches can handle nil values and provide safe type checking for interface values.

package main

import "fmt"

func processValue(value interface{}) {
fmt.Printf("Processing value: %v (type: %T)\n", value, value)

switch v := value.(type) {
case nil:
fmt.Println(" Nil value received")
case int:
fmt.Printf(" Integer: %d\n", v)
if v > 0 {
fmt.Printf(" Positive integer\n")
} else if v < 0 {
fmt.Printf(" Negative integer\n")
} else {
fmt.Printf(" Zero\n")
}
case float64:
fmt.Printf(" Float: %.2f\n", v)
case string:
fmt.Printf(" String: %s (length: %d)\n", v, len(v))
case bool:
fmt.Printf(" Boolean: %t\n", v)
default:
fmt.Printf(" Unknown type: %T\n", v)
}
fmt.Println()
}

func main() {
// Test with different values including nil
testValues := []interface{}{
nil,
42,
-15,
0,
3.14159,
"hello",
"",
true,
false,
[]int{1, 2, 3},
}

fmt.Println("Testing type switches with nil handling:")
for _, value := range testValues {
processValue(value)
}

// Safe type assertion with type switch
var value interface{} = "test"

switch v := value.(type) {
case string:
fmt.Printf("Safe string conversion: %s\n", v)
case nil:
fmt.Println("Value is nil")
default:
fmt.Printf("Value is not a string: %T\n", v)
}
}

Advanced Switch Patterns

Switch with Complex Expressions

Go allows you to use complex expressions in switch statements, making them extremely flexible for various programming scenarios.

package main

import (
"fmt"
"strings"
"time"
)

func main() {
// Switch with complex expressions
currentHour := time.Now().Hour()

switch {
case currentHour >= 6 && currentHour < 12:
fmt.Println("Good morning!")
case currentHour >= 12 && currentHour < 18:
fmt.Println("Good afternoon!")
case currentHour >= 18 && currentHour < 22:
fmt.Println("Good evening!")
default:
fmt.Println("Good night!")
}

// Switch with string operations
userInput := " Hello World "
trimmed := strings.TrimSpace(userInput)

switch {
case strings.HasPrefix(trimmed, "Hello"):
fmt.Println("Greeting detected")
case strings.HasPrefix(trimmed, "Goodbye"):
fmt.Println("Farewell detected")
case strings.Contains(trimmed, "help"):
fmt.Println("Help request detected")
default:
fmt.Println("Unknown input")
}

// Switch with mathematical expressions
x, y := 10, 20

switch {
case x+y > 30:
fmt.Println("Sum is greater than 30")
case x*y > 150:
fmt.Println("Product is greater than 150")
case x > y:
fmt.Println("x is greater than y")
case x < y:
fmt.Println("x is less than y")
default:
fmt.Println("x equals y")
}
}

Switch with Function Calls

Switch statements can include function calls in both the switch expression and case values, enabling dynamic behavior.

package main

import (
"fmt"
"math"
"strings"
)

// Helper functions for switch examples
func getTemperature() int {
return 25
}

func getHumidity() int {
return 60
}

func getUserRole() string {
return "admin"
}

func calculateScore() int {
return 85
}

func main() {
// Switch with function calls
switch {
case getTemperature() > 30:
fmt.Println("Hot weather detected")
case getHumidity() > 70:
fmt.Println("High humidity detected")
default:
fmt.Println("Normal weather conditions")
}

// Switch with calculated values
switch role := getUserRole(); role {
case "admin":
fmt.Println("Administrator privileges granted")
case "moderator":
fmt.Println("Moderator privileges granted")
case "user":
fmt.Println("User privileges granted")
default:
fmt.Println("Guest privileges granted")
}

// Switch with mathematical functions
angle := 45.0

switch {
case math.Sin(angle*math.Pi/180) > 0.5:
fmt.Println("High sine value")
case math.Cos(angle*math.Pi/180) > 0.5:
fmt.Println("High cosine value")
default:
fmt.Println("Normal trigonometric values")
}

// Switch with string functions
text := "Hello World"

switch {
case strings.ToUpper(text) == text:
fmt.Println("All uppercase")
case strings.ToLower(text) == text:
fmt.Println("All lowercase")
case strings.Contains(text, " "):
fmt.Println("Contains spaces")
default:
fmt.Println("Mixed case text")
}
}

Switch with Range Conditions

Switch statements can be used with range conditions to handle numeric ranges elegantly.

package main

import "fmt"

func main() {
// Switch with range conditions
age := 25

switch {
case age >= 0 && age < 13:
fmt.Println("Child")
case age >= 13 && age < 20:
fmt.Println("Teenager")
case age >= 20 && age < 60:
fmt.Println("Adult")
case age >= 60:
fmt.Println("Senior")
default:
fmt.Println("Invalid age")
}

// Switch with score ranges
score := 85

switch {
case score >= 90:
fmt.Println("Grade: A")
case score >= 80:
fmt.Println("Grade: B")
case score >= 70:
fmt.Println("Grade: C")
case score >= 60:
fmt.Println("Grade: D")
default:
fmt.Println("Grade: F")
}

// Switch with temperature ranges
temperature := 22

switch {
case temperature < 0:
fmt.Println("Freezing")
case temperature >= 0 && temperature < 10:
fmt.Println("Cold")
case temperature >= 10 && temperature < 20:
fmt.Println("Cool")
case temperature >= 20 && temperature < 30:
fmt.Println("Warm")
case temperature >= 30:
fmt.Println("Hot")
}
}

Best Practices for Switch Statements

Choosing Between if/else and switch

package main

import "fmt"

func main() {
// When to use switch vs if/else

// Good use of switch: Multiple discrete values
day := "Monday"

// Switch is better for this case
switch day {
case "Monday":
fmt.Println("Start of work week")
case "Tuesday":
fmt.Println("Second day")
case "Wednesday":
fmt.Println("Mid-week")
case "Thursday":
fmt.Println("Almost Friday")
case "Friday":
fmt.Println("TGIF!")
default:
fmt.Println("Weekend or unknown")
}

// Good use of if/else: Complex conditions
age := 25
hasLicense := true
hasInsurance := true

// if/else is better for complex boolean logic
if age >= 18 && hasLicense && hasInsurance {
fmt.Println("Can drive legally")
} else if age >= 16 && hasLicense {
fmt.Println("Can drive with supervision")
} else if age < 16 {
fmt.Println("Too young to drive")
} else {
fmt.Println("Need license and insurance")
}

// Good use of switch: Type checking
var value interface{} = 42

switch v := value.(type) {
case int:
fmt.Printf("Integer: %d\n", v)
case string:
fmt.Printf("String: %s\n", v)
default:
fmt.Printf("Other type: %T\n", v)
}
}

Performance Considerations

package main

import "fmt"

func main() {
// Performance considerations for switch statements

// Switch is generally faster than if/else chains
// because Go can optimize switch statements better

value := 5

// Efficient switch with integer values
switch value {
case 1:
fmt.Println("One")
case 2:
fmt.Println("Two")
case 3:
fmt.Println("Three")
case 4:
fmt.Println("Four")
case 5:
fmt.Println("Five")
default:
fmt.Println("Other")
}

// Less efficient: Complex expressions in switch
// (Go has to evaluate expressions for each case)
x, y := 10, 20

switch {
case x+y > 30:
fmt.Println("Sum > 30")
case x*y > 150:
fmt.Println("Product > 150")
case x > y:
fmt.Println("x > y")
default:
fmt.Println("Default case")
}

// More efficient: Pre-calculate values
sum := x + y
product := x * y

switch {
case sum > 30:
fmt.Println("Sum > 30")
case product > 150:
fmt.Println("Product > 150")
case x > y:
fmt.Println("x > y")
default:
fmt.Println("Default case")
}
}

Code Organization and Readability

package main

import "fmt"

func main() {
// Code organization and readability best practices

// 1. Use descriptive case values
userRole := "admin"

switch userRole {
case "administrator", "admin":
fmt.Println("Full system access")
case "moderator", "mod":
fmt.Println("Content moderation access")
case "user", "member":
fmt.Println("Standard user access")
case "guest", "visitor":
fmt.Println("Limited access")
default:
fmt.Println("Unknown role - no access")
}

// 2. Group related cases together
month := "December"

switch month {
// Winter months
case "December", "January", "February":
fmt.Println("Winter season")

// Spring months
case "March", "April", "May":
fmt.Println("Spring season")

// Summer months
case "June", "July", "August":
fmt.Println("Summer season")

// Autumn months
case "September", "October", "November":
fmt.Println("Autumn season")

default:
fmt.Println("Invalid month")
}

// 3. Use default cases appropriately
status := "active"

switch status {
case "active":
fmt.Println("User is active")
case "inactive":
fmt.Println("User is inactive")
case "suspended":
fmt.Println("User is suspended")
case "banned":
fmt.Println("User is banned")
default:
fmt.Println("Unknown status - treating as inactive")
}
}

What You've Learned

Congratulations! You now have a comprehensive understanding of Go's switch statements:

Expression Switches

  • Basic expression switches with discrete values
  • Switch statements with expressions and calculations
  • Variable declaration within switch statements
  • Multiple case values and complex expressions

Fallthrough Behavior

  • Default no-fallthrough behavior in Go
  • Explicit fallthrough statements when needed
  • Conditional fallthrough patterns
  • Comparison with traditional fallthrough languages

Type Switches

  • Basic type switches for interface values
  • Type switches with custom types and interfaces
  • Nil handling in type switches
  • Safe type checking and conversion

Advanced Patterns

  • Complex expressions in switch statements
  • Function calls in switch statements
  • Range conditions and mathematical expressions
  • String operations and pattern matching

Best Practices

  • Choosing between switch and if/else statements
  • Performance considerations and optimization
  • Code organization and readability
  • Proper use of default cases and fallthrough

Next Steps

You now have a solid foundation in Go's switch statements. In the next section, we'll explore Go's loop structures, including for loops, range loops, and different iteration patterns that will enable you to repeat operations and process collections of data.

Understanding switch statements is crucial for handling multiple conditions elegantly and writing maintainable code. These concepts form the foundation for all the more advanced programming techniques we'll cover in the coming chapters.


Ready to learn about loop structures? Let's explore Go's powerful loop constructs and learn how to iterate over data effectively!