Switch Statements
Learn how Go's switch statement works, why it doesn't fall through by default, and how to use it as a clean alternative to long if/else chains.
Introduction
A long chain of if/else if statements can quickly become hard to read. Go offers a cleaner alternative: the switch statement. Go's switch is more flexible than the switch you may have seen in C, Java, or JavaScript — it doesn't require a "break" at the end of every case, it can compare against expressions instead of just constants, and it can even be written without a condition at all, acting as a tidy replacement for if/else chains.
- The basic syntax of a switch statement in Go.
- Why Go switches do not fall through by default.
- How to opt into fallthrough behavior explicitly.
- How to match multiple values in a single case.
- How to write a condition-less switch.
- A first glimpse at type switches, covered fully with interfaces.
Basic Switch Syntax
A switch statement evaluates an expression once and compares it against a list of case values. When a match is found, that case's block runs and the switch exits.
package main
import "fmt"
func main() { day := "Tuesday"
switch day { case "Monday": fmt.Println("Start of the work week.") case "Tuesday": fmt.Println("Second day of the week.") case "Saturday", "Sunday": fmt.Println("It's the weekend!") default: fmt.Println("Just another day.") }}Click Run to see what this code prints.
Notice that "Saturday" and "Sunday" are combined into a single case using a comma-separated list — Go lets you match several values with one block instead of duplicating code.
No Automatic Fallthrough
In languages like C and Java, execution "falls through" into the next case unless you explicitly add a break statement. This is a common source of bugs — forgetting a single break can silently execute unintended cases. Go flips this default: each case automatically breaks after its block runs, so you never need to write break yourself.
package main
import "fmt"
func main() { score := 85
switch { case score >= 90: fmt.Println("Grade: A") case score >= 80: fmt.Println("Grade: B") case score >= 70: fmt.Println("Grade: C") default: fmt.Println("Grade: F") }}Click Run to see what this code prints.
Once the "score >= 80" case matches, Go runs that block and immediately exits the switch. It never checks "score >= 70" or the default case, even though those conditions were never explicitly excluded.
The fallthrough Keyword
Sometimes you deliberately want execution to continue into the next case. Go provides the fallthrough keyword for exactly this — it must be the last statement in a case block, and it unconditionally runs the next case's body, regardless of whether that case's value matches.
package main
import "fmt"
func main() { num := 1
switch num { case 1: fmt.Println("One") fallthrough case 2: fmt.Println("Two (reached via fallthrough)") case 3: fmt.Println("Three (not reached)") }}Click Run to see what this code prints.
fallthrough ignores the matching logic of the next case entirely — it always executes, even if that case's value wouldn't normally match. This can be surprising to readers, so reach for it only when it genuinely simplifies the code.
Multiple Values in a Case
As shown earlier, a single case can list multiple comma-separated values. The case runs if the switch expression matches any one of them — this is a clean way to group related conditions without fallthrough.
package main
import "fmt"
func classifyChar(c byte) string { switch c { case 'a', 'e', 'i', 'o', 'u': return "vowel" case ' ', ' ', '': return "whitespace" default: return "consonant or symbol" }}
func main() { fmt.Println(classifyChar('e')) fmt.Println(classifyChar('k'))}Click Run to see what this code prints.
Switch With No Condition
When you omit the expression after switch, Go treats it as switch true — each case is a boolean expression, and the first one that evaluates to true runs. This is often more readable than a long if/else if chain because the cases line up visually and each condition is independent.
package main
import "fmt"
func main() { age := 15
switch { case age < 13: fmt.Println("Child") case age < 20: fmt.Println("Teenager") case age < 65: fmt.Println("Adult") default: fmt.Println("Senior") }}Click Run to see what this code prints.
A Peek at Type Switches
Go also supports a special form called a type switch, which checks the dynamic type stored in an interface value rather than comparing values. You will cover this in depth once interfaces are introduced, but here is a taste of what it looks like.
package main
import "fmt"
func describe(i interface{}) { switch v := i.(type) { case int: fmt.Printf("%d is an int\n", v) case string: fmt.Printf("%q is a string\n", v) default: fmt.Printf("%v is some other type\n", v) }}
func main() { describe(42) describe("hello")}Click Run to see what this code prints.
Type switches rely on interfaces, which you'll learn about in a later lesson. For now, just recognize the i.(type) syntax when you see it.
Common Mistakes
- Adding a break statement out of habit — it is unnecessary and Go will warn that it is unreachable if placed after other code.
- Assuming a switch falls through like in C or JavaScript — in Go you must opt in with fallthrough.
- Forgetting that fallthrough executes the next case unconditionally, even if its value wouldn't normally match.
- Writing a long if/else if chain when a condition-less switch would be clearer.
- Placing fallthrough anywhere except as the very last statement in a case — it will not compile elsewhere.
Best Practices
- Prefer switch over long if/else if chains for readability once you have three or more conditions.
- Use comma-separated values in a case instead of duplicating logic across multiple cases.
- Reserve fallthrough for the rare cases where cascading behavior is genuinely intended, and add a comment explaining why.
- Always include a default case when handling user input or external data, so unexpected values are caught explicitly.
- Use a condition-less switch instead of nested if statements for range-based checks like grading or bucketing.
Frequently Asked Questions
No. Each case automatically breaks after running. Writing break is legal but redundant.
Yes — Go's switch works with any comparable type, including strings, numbers, and even custom types, not just integers.
Nothing happens. The switch simply does nothing and execution continues after it.
For a small number of branches the performance difference is negligible. Choose switch primarily for readability, not speed.
Key Takeaways
- Go's switch does not fall through by default — each case exits automatically after running.
- Use fallthrough to explicitly opt into cascading into the next case.
- A single case can match multiple comma-separated values.
- A switch with no condition after the switch keyword behaves like a cleaner if/else if chain.
- Type switches use the i.(type) syntax to branch on an interface value's dynamic type.
Summary
Go's switch statement trades the fallthrough-by-default behavior of C-style languages for something safer and more predictable, while still giving you an explicit escape hatch when cascading logic is genuinely useful. Combined with condition-less switches, it becomes a powerful, readable alternative to sprawling if/else chains. Next, you'll move into Go's collection types, starting with arrays.