Go Guide

Go, chapter by chapter · Lesson 4 of 62 · about 35 min

Control structures

for, if and switch: Go's only three control statements, how each form runs step by step, and the patterns you'll use in every algorithm.

On this page · 8 sections
  1. 01for: the only loop
  2. 02if
  3. 03switch
  4. 04More examples
  5. 05The full program
  6. 06Summary
  7. 07Check your understanding
  8. 08Practice in Go

for: the only loop#

Form 1: the counting loop

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

It has three parts separated by semicolons. Here is exactly what happens:

StepActioniOutput
1init: i := 11
2condition i <= 3? yes, run body11
3post: i++2
4condition? yes, run body22
5post3
6condition? yes, run body33
7post4
8condition? no, exit4

The condition is checked before each iteration, so a loop can run zero times.

Form 2: the while loop

Drop the init and post parts and keep only the condition:

Go
n := 100
steps := 0
for n > 1 {
	n /= 2
	steps++
}

Form 3: the infinite loop

for { … } runs until something inside it calls break or return. Servers and event loops use this form.

Form 4: range

range walks over a collection. What you get depends on what you range over:

Range overYou get
slice or arrayindex, value
stringbyte index, rune
mapkey, value (random order)
channeleach value until the channel is closed
integer n (Go 1.22+)0, 1, …, n-1

Drop what you do not need: for i := range s gives indexes only, for _, v := range s gives values only.

break, continue and labels

break leaves the loop; continue skips to the next iteration. In nested loops, both apply to the innermost loop unless you name an outer one with a label:

Go
outer:
	for r := range grid {
		for c := range grid[r] {
			if grid[r][c] == target {
				fmt.Println("found at", r, c)
				break outer // leaves both loops
			}
		}
	}

if#

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

No parentheses around the condition, braces always required, and the condition must be a bool (Go will not treat 0 or "" as false).

An if can start with a short statement whose variables live only inside the if/else chain:

Go
if n, err := strconv.Atoi(s); err != nil {
	fmt.Println("not a number")
} else {
	fmt.Println(n * 2)
}

switch#

switch compares a value against several cases. The first match runs, and then the switch ends; there is no fall-through.

Go
switch day {
case "sat", "sun":
	fmt.Println("weekend")
case "fri":
	fmt.Println("almost")
default:
	fmt.Println("workday")
}

A switch with no value is a clean replacement for long if-else chains. Each case is a condition, checked top to bottom:

Go
switch {
case score >= 90:
	grade = "A"
case score >= 80:
	grade = "B"
default:
	grade = "C"
}

More examples#

The book's exercises: FizzBuzz and multiples of three

exercises.go
Go
package main
 
import (
	"fmt"
	"strings"
)
 
func main() {
	// Print 1 to 15; multiples of 3 say Fizz, of 5 say Buzz, of both say FizzBuzz.
	var out []string
	for i := 1; i <= 15; i++ {
		switch {
		case i%15 == 0:
			out = append(out, "FizzBuzz")
		case i%3 == 0:
			out = append(out, "Fizz")
		case i%5 == 0:
			out = append(out, "Buzz")
		default:
			out = append(out, fmt.Sprint(i))
		}
	}
	fmt.Println(strings.Join(out, " "))
 
	// The numbers from 1 to 100 that are divisible by 3, counted and summed.
	count, sum := 0, 0
	for i := 3; i <= 100; i += 3 { // step by 3 instead of testing every number
		count++
		sum += i
	}
	fmt.Println(count, "multiples of 3, sum", sum)
 
	// Even or odd for 1..10, the chapter's warm-up.
	for i := range 10 {
		n := i + 1
		parity := "odd"
		if n%2 == 0 {
			parity = "even"
		}
		fmt.Print(n, ":", parity, " ")
	}
	fmt.Println()
}
Output
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
33 multiples of 3, sum 1683
1:odd 2:even 3:odd 4:even 5:odd 6:even 7:odd 8:even 9:odd 10:even

switch details: fallthrough, no condition, and init statements

switches.go
Go
package main
 
import (
	"fmt"
	"time"
)
 
func describe(n int) string {
	switch {
	case n < 0:
		return "negative"
	case n == 0:
		return "zero"
	case n%2 == 0:
		return "positive even"
	default:
		return "positive odd"
	}
}
 
func main() {
	for _, n := range []int{-3, 0, 4, 7} {
		fmt.Printf("%d is %s\n", n, describe(n))
	}
 
	// Cases do not fall through by default. fallthrough opts in, unconditionally.
	switch level := 2; level {
	case 1:
		fmt.Println("level 1 setup")
		fallthrough
	case 2:
		fmt.Println("level 2 setup")
		fallthrough
	case 3:
		fmt.Println("level 3 setup")
	}
 
	switch d := time.Date(2026, 10, 17, 0, 0, 0, 0, time.UTC).Weekday(); d {
	case time.Saturday, time.Sunday:
		fmt.Println(d, "is a weekend")
	default:
		fmt.Println(d, "is a weekday")
	}
}
Output
-3 is negative
0 is zero
4 is positive even
7 is positive odd
level 2 setup
level 3 setup
Saturday is a weekend

Loops in modern Go: ranging over integers and per-iteration variables

Since Go 1.22, for i := range n counts from 0 to n-1, and each iteration of a for loop gets a fresh copy of its loop variables. Before 1.22, all the closures below would have captured the same i and printed 3 three times.

loops.go
Go
package main
 
import "fmt"
 
func main() {
	for i := range 3 {
		fmt.Print(i, " ")
	}
	fmt.Println()
 
	var funcs []func() int
	for i := 0; i < 3; i++ {
		funcs = append(funcs, func() int { return i }) // each closure gets its own i (Go 1.22+)
	}
	for _, f := range funcs {
		fmt.Print(f(), " ")
	}
	fmt.Println()
 
	// Ranging over a string yields runes and their byte offsets.
	for i, r := range "añb" {
		fmt.Printf("%d:%c ", i, r)
	}
	fmt.Println()
 
	// A labeled continue skips to the next iteration of the OUTER loop.
	rows := [][]int{{1, 2, -1, 4}, {5, 6, 7}, {-2, 9}}
outer:
	for r, row := range rows {
		for _, v := range row {
			if v < 0 {
				fmt.Printf("row %d has a negative, skipping the rest of it\n", r)
				continue outer
			}
		}
		fmt.Printf("row %d is all non-negative\n", r)
	}
}
Output
0 1 2 
0 1 2 
0:a 1:ñ 3:b 
row 0 has a negative, skipping the rest of it
row 1 is all non-negative
row 2 has a negative, skipping the rest of it

The full program#

control.go
Go
package main
 
import (
	"fmt"
	"strconv"
)
 
func fizzBuzz(n int) []string {
	out := make([]string, 0, n)
	for i := 1; i <= n; i++ {
		switch {
		case i%15 == 0:
			out = append(out, "FizzBuzz")
		case i%3 == 0:
			out = append(out, "Fizz")
		case i%5 == 0:
			out = append(out, "Buzz")
		default:
			out = append(out, strconv.Itoa(i))
		}
	}
	return out
}
 
func classify(n int) string {
	if n < 0 {
		return "negative" // early return
	}
	if n%2 == 0 {
		return "even"
	}
	return "odd"
}
 
func main() {
	fmt.Println(fizzBuzz(15))
 
	n, steps := 100, 0
	for n > 1 {
		n /= 2
		steps++
	}
	fmt.Println("halvings:", steps)
 
	for i := range 3 {
		fmt.Print(classify(i-1), " ")
	}
	fmt.Println()
 
	grid := [][]int{{1, 2, 3}, {4, 5, 6}}
outer:
	for r, row := range grid {
		for c, v := range row {
			if v == 5 {
				fmt.Println("found 5 at", r, c)
				break outer
			}
		}
	}
}
Output
[1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz]
halvings: 6
negative even odd 
found 5 at 1 1

Why 6 halvings? 100 → 50 → 25 → 12 → 6 → 3 → 1. That repeated halving is exactly what makes binary search fast, which you will meet in Module 2.

Summary#

  • for is the only loop: counting, while-style, infinite and range forms.
  • range gives index and value for slices, rune for strings, key and value for maps, and counts for integers.
  • if needs a real bool, can declare short-lived variables, and works best with early returns.
  • switch stops at the first match; a bare switch replaces if-else chains.
  • Labels let break and continue target an outer loop.

Check your understanding

Answer before scrolling back up. If you miss one, reread the matching section, then try again.

  1. Q1.Which loop keywords does Go have?

  2. Q2.Does a Go switch case fall through to the next one?

  3. Q3.for i := range 3 { fmt.Print(i) } prints:

  4. Q4.In if v, ok := m[k]; ok { ... }, where can you use v?

Practice in Go

Write a table test first, then the solution. On LeetCode, choose Go as the language. Open a hint only after 15 minutes stuck.

  • Show hint

    Check divisibility by 15 first, or the 3 and 5 cases will catch it.

  • Show hint

    A loop works, but there is an O(1) formula: (high+1)/2 - low/2.

  • Show hint

    A while-style for n > 0 loop with an if/else inside.

  • Multiplication gridbuild

    Print a 9×9 multiplication table with aligned columns, then stop early at the first product over 50 using a labeled break.

    Show hint

    fmt.Printf("%4d", n) pads to width 4. Put outer: before the outer loop and write break outer.

Finished this lesson?

Mark it done to track your progress. It is saved in this browser only.