> series: learn go from 0 to 100 | article 3 of 19
You’ve got types and variables from the previous article. Today: Go control structures. The news: Go has fewer pieces than any language you’ve used. The surprise: you won’t miss a single one.
if: no parentheses, one trick
Go’s if drops the parentheses and demands braces always, whether it’s one line or twenty (A Tour of Go, if):
// No parentheses, braces mandatory.
if age >= 18 {
fmt.Println("come in")
} else {
fmt.Println("out")
}
// With an init statement:
// declare, check and scope-limit in one shot.
if v := compute(); v > threshold {
fmt.Println("v only exists here", v)
}
The second form is gold: the init statement declares a variable that only lives inside the if and its else (A Tour of Go, if with a short statement). When we reach error handling (article 9) you’ll see this pattern absolutely everywhere.
Heads up, JS and Java folks: there is no ternary operator. That’s deliberate, so complex conditions don’t end up squeezed into one unreadable line (official FAQ).
for: the language’s only loop
Go has no while, no do-while, no forEach. It has for, and its four faces cover everything (A Tour of Go, for and spec):
// 1. Classic
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// 2. While style: condition only
for balance > 0 {
balance = spend(balance)
}
// 3. Infinite: nothing at all (exit via break or return)
for {
process()
}
// 4. range: collections and more
nums := []int{2, 4, 8}
for i, v := range nums {
fmt.Println(i, v)
}
Form 2 is your lifelong while: drop the init and increment, keep the condition.
Form 4: range, iterates slices, maps, strings and channels, returning index and value.
The range operator can also iterate over channels, although we’ll take a closer look at that when we cover concurrency with goroutines later in this series.
Only want the value? Discard the index with _, the blank identifier from article 2.
Fewer pieces, fewer decisions: that’s the whole philosophy behind Go control structures. Python makes you choose between for, while and comprehensions. JS between for, for…of, for…in, forEach, map… In Go the code review conversation about which loop to use takes zero seconds.

switch: no accidental fallthrough
Go’s switch breaks with the C and Java tradition right where it hurts most, no implicit fallthrough. Each case ends on its own, no break needed (Effective Go, control structures). If you truly want to fall through, the explicit fallthrough keyword exists, out in the open (spec).
switch day {
case "saturday", "sunday": // several values per case
fmt.Println("weekend")
case "friday":
fmt.Println("almost")
default:
fmt.Println("back to code")
}
// Expressionless: a readable if/else if
switch {
case hour < 12:
fmt.Println("good morning")
case hour < 21:
fmt.Println("good afternoon")
default:
fmt.Println("good evening")
}
The expressionless switch is a very Go pattern. Each case is a boolean condition, replacing long if/else if chains (A Tour of Go, switch). Cases evaluate top to bottom and stop at the first match. And yes, it also takes an init statement, just like if.
There’s also a type switch for asking about types instead of values, but that gets its own post (article 7). And yes, goto exists in the language. Knowing it’s there and not using it is also a best practice.
The goto statement in Go only jumps within the same function and cannot jump over variable declarations, so its scope is quite limited in practice (language specification).
Weekly challenge: put Go control structures to work
- The classic: FizzBuzz from 1 to 100, but with an expressionless switch instead of if/else.
- Simulate a launch countdown with a while-style for counting down from 10.
- Bonus: walk the string “gopher” with range printing index and character. Remember what you see for article 8, where we’ll explain why indexes get weird with accented characters.
Go control structures: level complete.
Next article: functions. Multiple return values, variadic functions and closures.
> exit