Go Basics #3 Control Flow — if, for, switch
In #2 Variables, Types, Constants you saw the tools for handling values. This time — making branches and loops with those values.
if / else if / else
#
Conditional branching. Almost identical to other languages, except there are no parentheses.
score := 85
if score >= 90 {
fmt.Println("A")
} else if score >= 80 {
fmt.Println("B")
} else {
fmt.Println("C")
}It’s if condition, not if (condition). Coming from C or Java this feels odd at first, but you adjust quickly.
Short statement #
Inside the condition you can declare a variable and immediately use it. A pattern you’ll meet very often in Go.
if v, ok := lookup(key); ok {
fmt.Println("found:", v)
} else {
fmt.Println("not found")
}v, ok := lookup(key) happens inside the if, and v and ok are visible only within the if/else block. They disappear once the block exits.
This pattern shows up everywhere in Go code. It’s also the standard shape for error checking (#4).
Conditions are bool only
#
x := 5
// if x { ... } ✗ x is an int
if x > 0 { ... } // OK
// if v := lookup(); v { ... } ✗ v has to be a boolThere’s no automatic truthy/falsy conversion like JavaScript. Conditions must be explicit.
for — the only loop
#
Go has no while. Every loop is expressed with a single for.
1) Classic for — three parts #
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// 0, 1, 2, 3, 4Same shape as C/Java. Init ; condition ; post.
2) Like a while — condition only #
n := 0
for n < 5 {
fmt.Println(n)
n++
}Of the three parts, only the condition is present. Same meaning as a while in other languages.
3) Infinite loop — no condition #
for {
// ...
if shouldStop() {
break
}
}for { } is an infinite loop. Same as while (true).
4) for range — iterating collections
#
fruits := []string{"apple", "banana", "grape"}
for i, fruit := range fruits {
fmt.Println(i, fruit)
}
// 0 apple
// 1 banana
// 2 grapeages := map[string]int{
"Curtis": 30,
"Alice": 25,
}
for name, age := range ages {
fmt.Println(name, age)
}for i, r := range "안녕" {
fmt.Printf("%d: %c\n", i, r)
}
// 0: 안
// 3: 녕 (UTF-8 byte index)for range is very powerful. Drop the second variable when you only need the index/key, or use _ for the first when you only need the value.
for i := range fruits { // index only
fmt.Println(i)
}
for _, fruit := range fruits { // value only
fmt.Println(fruit)
}break and continue
#
Loop flow control.
for i := 0; i < 10; i++ {
if i == 5 {
break // exit the loop
}
if i%2 == 0 {
continue // jump to next iteration
}
fmt.Println(i)
}
// 1, 3Labels for nested loops #
When you want to break out of multiple nested loops at once.
outer:
for i := 0; i < 5; i++ {
for j := 0; j < 5; j++ {
if i*j > 6 {
break outer // break out to the outer label in one shot
}
}
}Not used often, but clean for deeply nested cases.
switch — Go’s biggest difference
#
Go’s switch does not fall through automatically. The opposite of C/Java.
day := "mon"
switch day {
case "mon", "tue", "wed", "thu", "fri":
fmt.Println("weekday")
case "sat", "sun":
fmt.Println("weekend")
default:
fmt.Println("?")
}Each case has an automatic break. You don’t write break like in old C. You can also group multiple values in one case with commas.
Forced fallthrough #
If you really want to fall through to the next case, use the fallthrough keyword.
n := 1
switch n {
case 1:
fmt.Println("one")
fallthrough
case 2:
fmt.Println("two")
}
// one
// twoAlmost never used. Only when the intent is unmistakable.
Conditional switch — short form of if/else #
A switch with no expression turns long if/else chains into something compact.
score := 85
switch {
case score >= 90:
fmt.Println("A")
case score >= 80:
fmt.Println("B")
case score >= 70:
fmt.Println("C")
default:
fmt.Println("F")
}Each case is a bool expression. Often nicer than a long if/else if chain.
switch + short statement #
Like if, switch allows a short statement.
switch v := getValue(); {
case v > 100:
fmt.Println("large")
case v > 0:
fmt.Println("positive")
default:
fmt.Println("zero or below")
}Type switch — branching on interface #
We’ll cover this in earnest in Intermediate #1 Interfaces, but switch is also used for type checking.
func describe(i interface{}) {
switch v := i.(type) {
case int:
fmt.Println("int:", v)
case string:
fmt.Println("string:", v)
case bool:
fmt.Println("bool:", v)
default:
fmt.Println("unknown")
}
}The special i.(type) syntax. It branches on which concrete type the interface holds.
goto — almost never
#
Go does have goto, but it’s almost never used. You’ll only see it in very specific cases (escaping nesting, cleanup code).
Short-if pitfall — braces are required #
Unlike JavaScript, Go always requires braces.
// if condition doSomething() ✗ compile error
if condition {
doSomething()
}Thanks to this design, JavaScript-style “fake-indentation if” pitfalls don’t exist at all.
Common patterns #
1) Error checking #
The if err != nil pattern is one of the shapes you’ll see most often in Go code.
file, err := os.Open("data.txt")
if err != nil {
return err
}
defer file.Close()
// ... normal processingCovered in detail in #4.
2) Key existence check #
ages := map[string]int{"Curtis": 30}
if age, ok := ages["Curtis"]; ok {
fmt.Println(age)
}map[key] can return two values — the value and an ok flag. This short-statement pattern is standard.
3) early return #
A pattern of returning early instead of nesting deep with if/else.
func process(s string) error {
if s == "" {
return fmt.Errorf("empty string")
}
if len(s) > 100 {
return fmt.Errorf("string too long")
}
// main logic — only one indent level
return nil
}The same pattern you saw in JavaScript #4 Functions. Recommended in Go too.
Wrap-up #
What we covered:
if condition { }— no parentheses, braces required- Short statement —
if v, ok := ...; ok { }shows up often - Conditions are bool only — no automatic truthy/falsy conversion
foris the only loop — no while/do-while- Four shapes — classic, condition-only, infinite, range
for rangeover slices/maps/strings (strings are rune)switchhas implicit break — fallthrough is explicit- Conditional switch cleans up if/else chains
i.(type)for type switch (preview)
In the next post (#4 Functions, Multiple Return, error Type) we cover Go’s functions — how they’re defined, multiple return, and most importantly the error-handling pattern.