Skip to content

Control Flow

A basic if in Go looks familiar, but parentheses around the condition are omitted — the braces are always required:

if x > 0 {
fmt.Println("positive")
}

Go adds an init statement separated by a semicolon before the condition. Variables declared in the init are scoped to the entire if/else block:

if err := doWork(); err != nil {
fmt.Println("error:", err)
return
}
// err is not in scope here

This pattern is idiomatic for operations that return (value, error) — it keeps the error variable close to the check and avoids polluting the outer scope.

Go has one loop keyword: for. It covers all three common loop shapes.

C-style loop — init; condition; post:

for i := 0; i < 5; i++ {
fmt.Println(i)
}

Condition-only loop (equivalent to while in other languages):

n := 1
for n < 100 {
n *= 2
}

Infinite loop — omit the condition entirely; use break to exit:

for {
if done() {
break
}
}

range iterates over arrays, slices, strings, maps, and channels. It returns an index and a value on each iteration:

fruits := []string{"apple", "banana", "cherry"}
for i, v := range fruits {
fmt.Println(i, v)
}
// Discard the index with _
for _, v := range fruits {
fmt.Println(v)
}
// Range over a string yields runes (Unicode code points), not bytes
for i, r := range "Go!" {
fmt.Println(i, r)
}

Go’s switch evaluates cases top to bottom and stops at the first match — there is no automatic fallthrough to the next case. You do not need break at the end of each case:

switch day {
case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
fmt.Println("weekday")
case "Saturday", "Sunday":
fmt.Println("weekend")
default:
fmt.Println("unknown")
}

To fall through explicitly, use the fallthrough keyword. To match multiple values, list them comma-separated in one case.

Switch-true (expressionless switch) acts as a cleaner if/else chain:

switch {
case score >= 90:
fmt.Println("A")
case score >= 80:
fmt.Println("B")
default:
fmt.Println("C or below")
}
package main
import "fmt"
func classify(n int) string {
switch {
case n < 0:
return "negative"
case n == 0:
return "zero"
case n < 10:
return "small"
default:
return "large"
}
}
func main() {
// C-style for loop
fmt.Println("-- C-style loop --")
for i := 1; i <= 3; i++ {
fmt.Println(i)
}
// Condition-only loop (while equivalent)
fmt.Println("-- condition-only loop --")
n := 1
for n < 10 {
n *= 2
}
fmt.Println("n =", n)
// for range over slice
fmt.Println("-- range over slice --")
fruits := []string{"apple", "banana", "cherry"}
for i, v := range fruits {
fmt.Println(i, v)
}
// switch
fmt.Println("-- switch classify --")
for _, val := range []int{-5, 0, 7, 42} {
fmt.Println(val, "->", classify(val))
}
// if with init statement
fmt.Println("-- if with init --")
if x := 16; x%2 == 0 {
fmt.Println(x, "is even")
} else {
fmt.Println(x, "is odd")
}
}
What is the Go equivalent of a while loop in other languages?
In Go's switch statement, what happens when a case matches but has no fallthrough keyword?
What scope do variables declared in an if init statement have?
When ranging over a string with `for i, r := range s`, what type is r?