Control Flow
📋 Jump to TakeawaysGo keeps control flow simple. if doesn't need parentheses, switch breaks automatically, and there's only one loop keyword: for. No while, no do...while, no forEach. Fewer tools, each one doing its job perfectly.
if/else
Go's if statements don't need parentheses around the condition.
x := 10
if x > 5 {
fmt.Println("big") // big
}You can include an init statement before the condition. The variable is scoped to the if/else block.
if err := doSomething(); err != nil {
fmt.Println(err)
} else {
fmt.Println("success")
}
// err is not accessible hereswitch
Go's switch doesn't need break. Each case breaks automatically. Use fallthrough if you want to fall into the next case.
day := "Monday"
switch day {
case "Monday":
fmt.Println("Start of week") // Start of week
case "Friday":
fmt.Println("Almost weekend")
default:
fmt.Println("Midweek")
}A single case can match multiple values, separated by commas.
switch day {
case "Saturday", "Sunday":
fmt.Println("Weekend")
default:
fmt.Println("Weekday")
}An expression-less switch acts like a clean if/else chain.
score := 85
switch {
case score >= 90:
fmt.Println("A")
case score >= 80:
fmt.Println("B") // B
default:
fmt.Println("C")
}Go has a special construct called a type switch that checks the concrete type of an interface value.
var val interface{} = "hello"
switch v := val.(type) {
case string:
fmt.Println("string:", v) // string: hello
case int:
fmt.Println("int:", v)
}for Loop
for is the only loop in Go. It covers all looping patterns.
Classic three-component form:
for i := 0; i < 5; i++ {
fmt.Println(i) // 0, 1, 2, 3, 4
}While-style, just a condition:
n := 1
for n < 100 {
n *= 2
}
fmt.Println(n) // 128Range over an integer (Go 1.22+). Iterates from 0 to n-1:
for i := range 5 {
fmt.Println(i) // 0, 1, 2, 3, 4
}If you don't need the index, drop it:
for range 3 {
fmt.Println("hello") // prints 3 times
}Infinite loop. Use break to exit:
for {
fmt.Println("runs once")
break
}range
range iterates over slices, maps, strings, and channels.
nums := []int{10, 20, 30}
for i, v := range nums {
fmt.Println(i, v) // 0 10, 1 20, 2 30
}Use _ to discard the index or value. Go requires you to use every variable you declare, so _ is the blank identifier that tells the compiler "I don't need this."
for _, v := range nums {
fmt.Println(v) // 10, 20, 30
}Ranging over a map gives key-value pairs in random order.
m := map[string]int{"a": 1, "b": 2}
for k, v := range m {
fmt.Println(k, v)
}Ranging over a string gives runes, not bytes.
for i, ch := range "Go!" {
fmt.Printf("%d: %c\n", i, ch) // 0: G, 1: o, 2: !
}break, continue, and Labels
continue skips to the next iteration. break exits the loop.
for i := 0; i < 5; i++ {
if i == 2 {
continue
}
fmt.Println(i) // 0, 1, 3, 4
}Labels let you break or continue an outer loop from inside a nested loop.
outer:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if j == 1 {
continue outer
}
fmt.Println(i, j) // 0 0, 1 0, 2 0
}
}Key Takeaways
ifstatements can include an init statement — great for error checksswitchcases break automatically; nobreakneededforis the only loop — it handles classic, while-style, and infinite loopsrangeworks on slices, maps, strings, and channels- Labels let you control nested loops with
breakandcontinue