Skip to content

03 - Decisions and Loops

What this session is

About an hour. You'll learn how to make your program decide between options (with if) and how to make it repeat something (with for). These two things are the building blocks of every program that does anything more than print a fixed message.

Decisions with if

The world's smallest decision:

package main

import "fmt"

func main() {
    age := 18
    if age >= 18 {
        fmt.Println("adult")
    } else {
        fmt.Println("minor")
    }
}

Run it. You'll see adult.

Now change age := 18 to age := 15 and run it again. You'll see minor.

What's happening:

  • if age >= 18 { ... } - run the code in the braces only if age >= 18 is true.
  • else { ... } - if the condition was false, run this code instead.

The condition is whatever's between if and {. It must evaluate to a bool - true or false.

Comparison operators

The operators that produce true/false:

Operator Meaning
== equal to
!= not equal to
< less than
<= less than or equal to
> greater than
>= greater than or equal to

A common mistake: writing = (one equals sign) when you mean == (two). = is assignment ("set this to that"); == is comparison ("does this equal that?"). Go will give a compile error if you mix them up; just notice it now.

Chaining decisions with else if

What if you have more than two cases?

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

Reads top to bottom. The first condition that's true wins; everything else is skipped. If none match, the else block runs.

Combining conditions: &&, ||, !

You can build bigger conditions out of smaller ones:

Operator Meaning Example
&& and (both true) age >= 18 && hasLicense
\|\| or (at least one true) isWeekend \|\| isHoliday
! not (flip true to false) !isReady

Example:

age := 25
hasLicense := true
if age >= 18 && hasLicense {
    fmt.Println("can drive")
}

The condition is true only when both halves are true.

Repetition: for

This is the part that takes a few tries to internalize. Read carefully.

Print the numbers 1 through 5:

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

That for line is doing three things, separated by semicolons:

  1. i := 1 - create a variable i starting at 1. (This happens once, before anything else.)
  2. i <= 5 - the condition that keeps the loop going. Checked before each round.
  3. i++ - what to do after each round. (i++ is shorthand for i = i + 1.)

The flow is:

  1. Set i = 1. Check 1 <= 5. True → run the body. Print 1. Then do i++i = 2.
  2. Check 2 <= 5. True → print 2. Then i = 3.
  3. ... continues ...
  4. Check 5 <= 5. True → print 5. Then i = 6.
  5. Check 6 <= 5. False → stop.

Output:

1
2
3
4
5

Type this in. Run it. Change 1 to 10 and <= 5 to <= 20. Change i++ to i = i + 2 and see what happens. The way to internalize loops is to mess with them.

Two shorter forms

The "keep going while X" form, when you don't have a counter:

n := 10
for n > 0 {
    fmt.Println(n)
    n = n - 1
}

This prints 10, 9, 8, ..., 1. There's no "init" or "after each round" part - just the condition. The body has to do whatever changes the condition, otherwise the loop never stops.

The "forever" form, when you'll stop from inside:

for {
    // runs until you break out
}

Useful in programs that wait for events, listen on a socket, etc. We'll see real uses later.

Breaking out early: break and continue

break stops the loop entirely.

continue skips to the next round, without running the rest of the body this time.

for i := 1; i <= 10; i++ {
    if i == 5 {
        break // stop the whole loop when i is 5
    }
    if i%2 == 0 {
        continue // skip the print for even numbers
    }
    fmt.Println(i)
}

Output: 1, 3. Why?

  • i=1: not 5, not even → print 1.
  • i=2: not 5, even → continue (skip print).
  • i=3: not 5, not even → print 3.
  • i=4: even → skip.
  • i=5: break → stop entirely.

Putting it together

A small program that classifies the numbers 1 to 10:

package main

import "fmt"

func main() {
    for i := 1; i <= 10; i++ {
        if i%2 == 0 {
            fmt.Println(i, "even")
        } else {
            fmt.Println(i, "odd")
        }
    }
}

Type and run. Read the output. Read the code. Look at each line and ask: which line produced this output?

Exercise

Type this in a new file called classify.go:

Write a program that, for each number from 1 to 20:

  • If the number is divisible by 3, print Fizz instead of the number.
  • If the number is divisible by 5, print Buzz instead.
  • If divisible by both 3 and 5, print FizzBuzz.
  • Otherwise print the number.

(This is the classic "FizzBuzz" problem. It's famous as a small interview question, and a very good exercise for cementing if/else if/else with a loop.)

Hint: check the "both 3 and 5" case first. Why? Think about what would happen if you checked "divisible by 3" first.

When you finish, the output should be:

1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz

Don't move on until your program prints exactly this.

What you might wonder

"What about while loops?" Other languages have a separate while keyword. Go doesn't - for condition { } does the same thing. One loop keyword, fewer things to remember.

"What about do-while?" Use:

for {
    // body that should run at least once
    if !condition {
        break
    }
}

"Why doesn't Go have i++ as an expression?" In some languages j = i++ is legal and confusing. In Go, i++ is a statement on its own line - it doesn't return a value. This rules out a category of bugs that show up in C and C++.

"Why braces on if even for one line?" In some languages you can write if (cond) doThing(); without braces. Go requires the braces always. This is opinionated but on purpose - a category of bugs (forgetting braces, then adding a second line that doesn't actually belong to the if) becomes impossible.

Done

You can now: - Make a program take different actions based on conditions (if, else if, else). - Combine conditions with &&, ||, !. - Repeat actions with for, in three forms. - Exit a loop early with break, skip an iteration with continue.

You now have the basic shapes that every program is built from. Combined with what you learned in Page 02, you can in principle write any program - just very, very long ones.

The next page is the abstraction that lets your programs stay short as they get bigger: functions.

Next: Functions →

Comments