The switch statement in Go picks one branch from several alternatives. It is the usual tool when you compare one value against many constants, when you want a cleaner boolean ladder than repeated if, or when you need to branch on the concrete type stored in an any or interface value.
For two-way logic, if and else stays fine; when several paths share the same shape, switch usually reads better.
Tested on: Go 1.22 on 64-bit Linux; listings were run with
go runwhile this article was revised.
Quick answer: switch runs one case without falling through
In Go, switch runs the first matching case body and then exits the whole switch. Execution does not continue into the next case unless you use the fallthrough keyword on purpose.
package main
import "fmt"
func main() {
day := "Sunday"
switch day {
case "Saturday", "Sunday":
fmt.Println("Weekend")
default:
fmt.Println("Weekday")
}
}Running it prints a single line for the matching branch (here the weekend path).
What Is a Switch Statement in Go?
A switch chooses one block to run from several options. It is often clearer than a long if / else if chain when each branch compares the same value or follows the same pattern.
Basic switch case syntax
The form switch expr { case v1: ... case v2: ... default: ... } compares expr to each case value using equality. The default branch runs when nothing else matches.
Why Go switch does not need break
In C-family languages, execution often drops into the next case unless you break. In Go, each case is independent: only the selected case runs, then control leaves the switch. You can still use break to exit the innermost switch early (for example inside a loop); use a label on the outer for and break Label if you need to leave the loop from inside a switch.
This long if chain:
package main
import "fmt"
func main() {
status := "stop"
if status == "start" {
fmt.Println("Starting")
} else if status == "stop" {
fmt.Println("Stopping")
} else if status == "restart" {
fmt.Println("Restarting")
} else {
fmt.Println("Unknown command")
}
}prints one line for status. The same idea with switch looks like this:
package main
import "fmt"
func main() {
status := "stop"
switch status {
case "start":
fmt.Println("Starting")
case "stop":
fmt.Println("Stopping")
case "restart":
fmt.Println("Restarting")
default:
fmt.Println("Unknown command")
}
}Both programs print Stopping for the same status value.
Basic Golang Switch Case Example
Match a single value
package main
import "fmt"
func main() {
day := "Monday"
switch day {
case "Monday":
fmt.Println("Start of the work week")
case "Friday":
fmt.Println("Almost weekend")
case "Saturday", "Sunday":
fmt.Println("Weekend")
default:
fmt.Println("Regular weekday")
}
}For day == "Monday" you get the first message only.
Add a default case
default runs when no case matches. It is optional but useful for validation and command parsing.
Use Multiple Values in One Case
List several values separated by commas so they share one body. That is typical for weekends, grouped HTTP status codes, or menu options that behave the same way.
package main
import "fmt"
func main() {
day := "Sunday"
switch day {
case "Saturday", "Sunday":
fmt.Println("Weekend")
case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
fmt.Println("Weekday")
default:
fmt.Println("Invalid day")
}
}With day set to Sunday, the weekend branch runs once.
Switch Without an Expression
Write switch { ... } with no value after switch. Each case is a boolean expression; the first true case wins. That replaces many if / else if ladders when the conditions are not all equality on one tag.
Replace long if-else chains
Use conditions directly in case statements
package main
import "fmt"
func main() {
score := 82
switch {
case score >= 90:
fmt.Println("Grade A")
case score >= 75:
fmt.Println("Grade B")
case score >= 60:
fmt.Println("Grade C")
default:
fmt.Println("Fail")
}
}For score == 82, the second branch matches and prints the B grade line.
Switch with an Initial Statement
You may declare a short variable in the switch line; it exists only for that switch. That keeps temporary values scoped to the decision.
package main
import "fmt"
func main() {
switch number := 10; {
case number%2 == 0:
fmt.Println("Even number")
default:
fmt.Println("Odd number")
}
}Here number is visible only inside the switch. The program reports that ten is even.
Type Switch in Go
A type switch inspects the dynamic type inside an any (or interface) value. The syntax switch v := x.(type) binds v with the concrete type in each single-type case.
Check the dynamic type of an interface value
package main
import "fmt"
func printType(value any) {
switch v := value.(type) {
case int:
fmt.Println("Integer:", v)
case string:
fmt.Println("String:", v)
case bool:
fmt.Println("Boolean:", v)
default:
fmt.Println("Unknown type")
}
}
func main() {
printType(100)
printType("golinuxcloud")
printType(true)
}You should see three lines, one per call, labeling integer, string, and boolean.
Handle multiple types in one type switch
When several concrete types should share logic, list them in one case. The switch form switch value.(type) (without a binding) is enough when you do not need a typed variable per branch.
package main
import "fmt"
func check(value any) {
switch value.(type) {
case int, int64, float64:
fmt.Println("Numeric value")
case string:
fmt.Println("String value")
default:
fmt.Println("Other value")
}
}
func main() {
check(10)
check("hello")
check(true)
}The three calls print numeric, string, and other in order.
fallthrough in Go Switch
Go does not enter the next case after a match. If you truly need the next case body to run as well, end the case with fallthrough. It continues without re-checking the next case condition, so it is easy to misuse.
When fallthrough is allowed
fallthrough is only for value switches, not type switches. It must be the last statement in the case block.
package main
import "fmt"
func main() {
number := 1
switch number {
case 1:
fmt.Println("One")
fallthrough
case 2:
fmt.Println("Two")
case 3:
fmt.Println("Three")
}
}You get One followed by Two because execution drops into the next case after fallthrough.
Why fallthrough should be used carefully
Prefer comma-separated cases when both branches would print the same kind of work:
package main
import "fmt"
func main() {
switch number := 2; number {
case 1, 2:
fmt.Println("One or Two")
case 3:
fmt.Println("Three")
}
}That prints the combined branch once without fallthrough.
Common Switch Statement Use Cases
Match command-line options
package main
import "fmt"
func main() {
command := "start"
switch command {
case "start":
fmt.Println("Starting service")
case "stop":
fmt.Println("Stopping service")
case "restart":
fmt.Println("Restarting service")
default:
fmt.Println("Unknown command")
}
}Handle HTTP status codes
package main
import "fmt"
func main() {
statusCode := 404
switch statusCode {
case 200:
fmt.Println("OK")
case 400:
fmt.Println("Bad request")
case 401, 403:
fmt.Println("Unauthorized or forbidden")
case 404:
fmt.Println("Not found")
case 500:
fmt.Println("Server error")
default:
fmt.Println("Unhandled status code")
}
}Check weekdays and weekends
package main
import "fmt"
func main() {
day := "Saturday"
switch day {
case "Saturday", "Sunday":
fmt.Println("Weekend")
default:
fmt.Println("Weekday")
}
}Classify numbers with a tagless switch
package main
import "fmt"
func main() {
n := -5
switch {
case n < 0:
fmt.Println("Negative")
case n == 0:
fmt.Println("Zero")
default:
fmt.Println("Positive")
}
}break and labeled break in a loop
Inside a switch, break exits the innermost switch, not an outer for. Use a label on the loop when you need to stop the loop from inside a case.
package main
import "fmt"
func main() {
Outer:
for i := 0; i < 5; i++ {
switch {
case i == 2:
fmt.Println("break outer at", i)
break Outer
default:
fmt.Println("i=", i)
}
}
fmt.Println("done")
}The loop exits when i reaches two, then done prints.
Common Mistakes with Go Switch
Expecting automatic fallthrough
Only the matching case runs. If n is 1, a value switch with case 1: and case 2: does not run case 2 unless you add fallthrough.
Using fallthrough when multiple values fit better
Prefer case "Saturday", "Sunday": instead of chaining fallthrough across weekend cases.
Using fallthrough in a type switch
The compiler rejects fallthrough in a type switch. Split types or share logic with helper functions instead.
Ordering cases in a tagless switch
Cases are evaluated top to bottom; the first true branch wins. Put stricter conditions first.
package main
import "fmt"
func main() {
score := 95
switch {
case score >= 90:
fmt.Println("Excellent")
case score >= 60:
fmt.Println("Pass")
default:
fmt.Println("Fail")
}
}If you swap the two threshold cases, a score of 95 would hit the weaker test first and never reach Excellent.
Growing a switch without bound
Very large switches get hard to review. When each case maps to independent behavior, consider a map of handlers, small functions, or types that implement a shared interface.
Go Switch Cheat Sheet
| Goal | Pattern | Example idea |
|---|---|---|
| Match one expression | switch v { case ... } |
switch cmd { case "start": ... } |
| Several constants, same body | Comma-separated | case "Sat", "Sun": |
| Fallback | default |
default: fmt.Println("unknown") |
| Boolean ladder | Tagless switch { ... } |
switch { case x > 10: ... } |
| Scoped setup | Initial statement | switch x := f(); x { ... } |
| Dynamic type | Type switch | switch v := x.(type) { case int: ... } |
| Force next case body | fallthrough |
Rare; document why |
Which switch pattern should you use?
- One comparable value and many constants: classic
switch value. - Unrelated boolean tests:
switch { case cond: }. - Behavior depends on concrete type inside
any: type switch. - Two or three branches:
if/elseis often enough.
Summary
Go’s switch covers golang switch case style questions in one construct: match a value with case and default, combine values with commas, write tagless switches for ranges and scores, add a short declaration before the tag when it helps, and use switch v := x.(type) for dynamic types. Cases do not fall through by default, so you rarely need break; use fallthrough only when the control flow is obvious to the next reader. Order tagless cases from specific to general, and refactor very large switches into maps or helpers when cases stop fitting on one screen.

