Control flow
if/else with init statements, Go's single for loop in three forms, and switch without fallthrough.
- Write if/else statements including the init statement form
- Use for as a traditional C-style loop, a while-style loop, and an infinite loop
- Iterate over slices and maps with range
- Write a switch statement and understand that Go cases do not fall through by default
The Fundamentals track covers control flow as the skeleton of any program: decisions, repetition, and early exits. Go's take is deliberately minimal. There is no while keyword, no do/while, no repeat/until. Everything that involves repetition uses the single keyword for. This might sound limiting, but it means you have fewer things to remember — and once you see the three forms, you'll have everything you need.
if / else
Go's if statement looks familiar:
score := 85
if score >= 90 {
fmt.Println("A")
} else if score >= 80 {
fmt.Println("B")
} else {
fmt.Println("C or below")
}Two things differ from C or Java:
- No parentheses around the condition. The Go formatter (
go fmt) removes them if you write them. - Braces are required — you cannot write a single-line
ifwithout braces.
The init statement
Go's if has an optional init statement separated from the condition by a semicolon. The init statement runs first, and any variable it declares is scoped to the if/else block:
if err := doSomething(); err != nil {
fmt.Println("failed:", err)
return
}
// err is not in scope hereThis pattern appears constantly in Go — call a function, capture the error, and handle it, all in one line. The variable err disappears after the block ends, which keeps the namespace clean.
Scoped variables reduce noise. When you write if v, err := f(); err != nil, neither v nor err pollutes the surrounding scope. Compare this to languages where you must declare them before the if and they remain visible for the rest of the function. Go's init statement is small, but it keeps code tidier.
for — Go's only loop
Go has one loop keyword: for. It handles every looping pattern.
Form 1: traditional (init; condition; post)
for i := 0; i < 5; i++ {
fmt.Println(i) // 0 1 2 3 4
}Identical to a C for loop. The init statement, condition, and post statement are all optional.
Form 2: condition only (while equivalent)
Omit the init and post to get a while loop:
n := 1
for n < 100 {
n *= 2
}
fmt.Println(n) // 128Form 3: infinite loop
Omit the condition entirely to loop forever. Use break to exit:
for {
input := readInput()
if input == "quit" {
break
}
process(input)
}continue skips the rest of the loop body and moves to the next iteration. Both break and continue work the same in all three forms.
Go has no while keyword on purpose. The language designers decided one loop primitive is enough. When you see for with a condition and no init/post, read it as a while loop. It becomes natural very quickly.
range — iterating over collections
The most common loop in real Go programs uses range to iterate over a slice, string, or map:
fruits := []string{"apple", "banana", "cherry"}
for i, fruit := range fruits {
fmt.Printf("%d: %s\n", i, fruit)
}range returns two values: the index and the element. If you only need the element, discard the index with _:
for _, fruit := range fruits {
fmt.Println(fruit)
}If you only need the index (for example, to modify elements in place), just omit the second variable:
for i := range fruits {
fruits[i] = strings.ToUpper(fruits[i])
}Ranging over a map gives you key-value pairs (in random order — Go map iteration is deliberately non-deterministic):
ages := map[string]int{"Alice": 30, "Bob": 25}
for name, age := range ages {
fmt.Printf("%s is %d\n", name, age)
}switch
switch dispatches on a value and matches the first case that equals it:
day := "Tuesday"
switch day {
case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
fmt.Println("Weekday")
case "Saturday", "Sunday":
fmt.Println("Weekend")
default:
fmt.Println("Unknown day")
}Key differences from C/Java switch:
- No fallthrough by default. Each case ends automatically — no
breakneeded. If you genuinely want fallthrough to the next case, writefallthroughexplicitly (rare). - Multiple values per case separated by commas.
- No condition needed — a
switchwith no expression acts like a chain ofif/else if:
x := 42
switch {
case x < 0:
fmt.Println("negative")
case x == 0:
fmt.Println("zero")
default:
fmt.Println("positive")
}switch without expression is idiomatic for range checks. When you have three or more conditions checking the same variable with different operators, a conditionless switch is often cleaner than a chain of if/else if. The compiler handles both identically.
Check your understanding
Knowledge check
- 1.How do you write a while loop in Go?
- 2.In Go, what happens when a switch case matches and executes its body?
- 3.When you range over a map in Go, in what order are the key-value pairs visited?
Do it yourself
Write a program that:
- Uses a
forloop withrangeto iterate over[]int{3, 7, 2, 9, 1, 5}. - Tracks the running maximum.
- Prints the maximum after the loop.
Then rewrite the same logic using a traditional for i := 0; ... loop and confirm you get the same answer.
go run main.go
go fmt ./...
go vet ./...Where to go next
You can make decisions, loop in all the ways you need, and dispatch on values cleanly. Next: arrays, slices, and maps — Go's built-in collection types and the operations that make them useful.