Back to Notes

Go Control Flow

Overview

Go's control flow for conditionals is intentionally minimal: if/else for branching and switch as a cleaner alternative to long if-else chains. No parentheses around conditions, braces always required. For loops, see [[Loops]].


if / else

The condition needs no parentheses. The opening brace must be on the same line as if — Go enforces this formatting.

x := 15

if x < 0 {
    fmt.Println("negative")
} else if x == 0 {
    fmt.Println("zero")
} else {
    fmt.Println("positive")
}
// positive

if with Short Statement

Run a short initialisation statement before the condition. The variable declared is scoped to the entire if/else block — not accessible outside.

// v is only available inside the if/else block
if v := compute(); v > 100 {
    fmt.Println("large:", v)
} else {
    fmt.Println("small:", v)
}
// v is not accessible here

// Most common use: scoped error check
if err := doSomething(); err != nil {
    log.Fatal(err)
}

Why useful: keeps error variables from polluting the surrounding scope.


switch

switch is a cleaner way to write a series of if/else comparisons. Go's switch auto-breaks — no fall-through by default. Cases need not be constants or integers.

os := "darwin"

switch os {
case "darwin":
    fmt.Println("macOS")
case "linux":
    fmt.Println("Linux")
case "windows":
    fmt.Println("Windows")
default:
    fmt.Printf("Unknown OS: %s\n", os)
}

switch with init statement

switch os := runtime.GOOS; os {
case "darwin":
    fmt.Println("macOS")
default:
    fmt.Println(os)
}

switch with no condition — replaces long if-else chains

Equivalent to switch true. Each case is an independent boolean expression — much cleaner than a chain of else if:

t := time.Now()

switch {
case t.Hour() < 12:
    fmt.Println("Good morning!")
case t.Hour() < 17:
    fmt.Println("Good afternoon.")
default:
    fmt.Println("Good evening.")
}

Multiple values per case

switch day {
case "Saturday", "Sunday":
    fmt.Println("Weekend")
default:
    fmt.Println("Weekday")
}

fallthrough — explicit fall to next case

By default cases don't fall through. Use fallthrough to explicitly continue to the next case (unconditional — the next case's condition is not checked):

switch n {
case 1:
    fmt.Println("one")
    fallthrough
case 2:
    fmt.Println("two or after one")   // prints this
    fallthrough
case 3:
    fmt.Println("three or after two") // also prints this
}

Gotchas

GotchaDetail
No parentheses around conditionsif (x > 0) is valid but the formatter removes them — just write if x > 0
Opening brace must be on same lineif x > 0 \n { is a syntax error
switch auto-breaksNo break needed between cases; fallthrough is the explicit opt-in
fallthrough is unconditionalIt falls through regardless of the next case's condition
Short-statement variable scopeif v := ...; condv is only alive inside the if/else block