8. Making decisions: `if` / `else`

📖 Reading · 7 min
💡 Every code box below is live — edit it and hit Run.

if runs a block of code only when a condition is true. Go's version has one small twist you'll come to love, and a couple of hard rules that keep the code clean.

The basics

package main

import "fmt"

func main() {
    temp := 30

    if temp > 25 {
        fmt.Println("It's warm.")
    } else {
        fmt.Println("It's cool.")
    }
}

Two things set Go apart from most languages:

  1. No parentheses around the condition. if temp > 25, not if (temp > 25).
  2. Braces are mandatory, even for a single line. There's no brace-less if. This alone prevents a famous category of bugs.

The condition must be a bool. You can't write if temp hoping non-zero means true — Go requires an actual boolean expression.

else if chains

package main

import "fmt"

func main() {
    score := 82

    if score >= 90 {
        fmt.Println("A")
    } else if score >= 80 {
        fmt.Println("B")
    } else if score >= 70 {
        fmt.Println("C")
    } else {
        fmt.Println("F")
    }
}

Note the style Go enforces: } else if and } else { sit on the same line as the closing brace. gofmt (Go's formatter) will insist on it.

The twist: an if with its own variable

An if can declare a variable that lives only inside the if/else. The form is if <statement>; <condition>:

package main

import "fmt"

func main() {
    if doubled := 21 * 2; doubled > 40 {
        fmt.Println(doubled, "is big")
    } else {
        fmt.Println(doubled, "is small")
    }
    // doubled does not exist out here
}

Why is this so common in Go? Because functions often return a value and an error together, and you want to check the error right there without leaking the variable into the rest of your function. You'll see this exact shape everywhere once we reach errors:

package main

import (
    "fmt"
    "strconv"
)

func main() {
    if n, err := strconv.Atoi("123"); err == nil {
        fmt.Println("parsed:", n)
    } else {
        fmt.Println("not a number:", err)
    }
}

Keeping n and err scoped to the if is idiomatic Go — it stops stale variables from hanging around.

No ternary operator

Go deliberately has no ? : expression. If you want a value chosen by a condition, you write a plain if. It's a few more lines, but every reader knows exactly what it does:

package main

import "fmt"

func main() {
    n := -8

    label := "positive"
    if n < 0 {
        label = "negative"
    }

    fmt.Println(n, "is", label)
}

Your turn

Given year := 2024, print whether it's a leap year. The rule: a year is a leap year if it's divisible by 4 and (not divisible by 100 or divisible by 400). Use the modulo operator % (remainder). Print exactly:

2024 is a leap year
package main

import "fmt"

func main() {
    year := 2024
    // decide leap vs not, then print "<year> is a leap year"
    // or "<year> is not a leap year"
}
package main

import "fmt"

func main() {
    year := 2024
    leap := year%4 == 0 && (year%100 != 0 || year%400 == 0)
    if leap {
        fmt.Printf("%d is a leap year\n", year)
    } else {
        fmt.Printf("%d is not a leap year\n", year)
    }
}

Next: the loop — Go has exactly one, and it does everything.