Skip to content

Control Flow

Go deliberately has one looping construct and no ternary operator. Fewer ways to do the same thing means less to read when you come back to code later.

Parentheses around the condition are dropped, but braces are mandatory:

if age >= 18 {
fmt.Println("adult")
} else if age >= 13 {
fmt.Println("teen")
} else {
fmt.Println("child")
}

if can also carry an initializer, scoped to the if/else chain — a common pattern for error checks:

if err := doSomething(); err != nil {
return err
}
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// "while" loop
n := 0
for n < 3 {
n++
}
// infinite loop
for {
break
}
// range over a slice
for i, v := range []string{"a", "b", "c"} {
fmt.Println(i, v)
}

Go’s switch doesn’t fall through by default (use fallthrough explicitly if you want it), and cases can be arbitrary expressions, not just constants:

switch {
case age < 13:
fmt.Println("child")
case age < 18:
fmt.Println("teen")
default:
fmt.Println("adult")
}

defer schedules a function call to run right before the surrounding function returns — most commonly used for cleanup:

func readFile(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
// ... use f ...
return nil
}

f.Close() runs even if a later return fires early, which makes resource cleanup hard to forget.