Cristhian Villegas
Backend9 min read1 views

Go Course #3: Control Flow — if, switch and Loops

Go Course #3: Control Flow — if, switch and Loops

Introduction: controlling program flow in Go

Welcome to Part 3 of the Go course for beginners. In the previous articles you learned how to declare variables and work with data types. Now we are going to learn something fundamental: how to make your program make decisions and repeat actions.

Go Logo

Go takes a minimalist approach to control flow. Unlike other languages, Go only has one type of loop (the for loop), and its if statement has a unique feature: it can include an initialization statement. These design choices make Go code cleaner and more predictable.

By the end of this article you will be able to write programs that react to different conditions, select between multiple options with switch, and repeat tasks using different variants of the for loop.

The if/else statement with init statement

The if statement in Go works similarly to other languages, but it has a feature that makes it special: you can include a short initialization statement before the condition.

go
1package main
2
3import "fmt"
4
5func main() {
6    age := 20
7
8    // Basic if
9    if age >= 18 {
10        fmt.Println("You are an adult")
11    } else {
12        fmt.Println("You are a minor")
13    }
14
15    // if with init statement
16    if grade := 85; grade >= 90 {
17        fmt.Println("Excellent")
18    } else if grade >= 70 {
19        fmt.Println("Passed")
20    } else {
21        fmt.Println("Failed")
22    }
23    // grade does not exist outside the if block
24}
Important: The variable declared in the if init statement (like grade above) only exists within the if/else block. This helps keep the scope as narrow as possible, which is a Go best practice.

Unlike C or Java, Go does not use parentheses around the condition, but braces are mandatory even for a single line of code.

go
1// This does NOT compile in Go
2if age >= 18
3    fmt.Println("Adult")
4
5// This is correct
6if age >= 18 {
7    fmt.Println("Adult")
8}

Comparison and logical operators

To build conditions you need comparison and logical operators. Go supports the following:

OperatorMeaningExample
==Equal to5 == 5 is true
!=Not equal to5 != 3 is true
>Greater than10 > 7 is true
<Less than3 < 8 is true
>=Greater or equal5 >= 5 is true
<=Less or equal4 <= 3 is false

Logical operators let you combine multiple conditions:

OperatorMeaningExample
&&Logical ANDtrue && false is false
||Logical ORtrue || false is true
!Logical NOT!true is false
go
1package main
2
3import "fmt"
4
5func main() {
6    age := 25
7    hasID := true
8
9    if age >= 18 && hasID {
10        fmt.Println("You can enter the event")
11    }
12
13    temperature := 35
14    if temperature < 0 || temperature > 40 {
15        fmt.Println("Extreme weather, stay home")
16    }
17}

Switch: elegant multi-way selection

The switch statement in Go is more powerful and flexible than in many other languages. It has three main variants.

Switch with expression: evaluates a variable against multiple values.

go
1package main
2
3import "fmt"
4
5func main() {
6    day := "tuesday"
7
8    switch day {
9    case "monday":
10        fmt.Println("Start of the week")
11    case "tuesday", "wednesday", "thursday":
12        fmt.Println("Midweek")
13    case "friday":
14        fmt.Println("Almost weekend")
15    case "saturday", "sunday":
16        fmt.Println("Weekend")
17    default:
18        fmt.Println("Invalid day")
19    }
20}
Tip: In Go, each case has an implicit break. You do not need to write break at the end of each case like in C or Java. If you want execution to continue to the next case, use the fallthrough keyword.

Switch without condition: replaces long if/else if chains. It is very readable.

go
1package main
2
3import "fmt"
4
5func classifyGrade(grade int) string {
6    switch {
7    case grade >= 90:
8        return "Excellent"
9    case grade >= 80:
10        return "Very good"
11    case grade >= 70:
12        return "Passed"
13    case grade >= 60:
14        return "Sufficient"
15    default:
16        return "Failed"
17    }
18}
19
20func main() {
21    fmt.Println(classifyGrade(85)) // Very good
22    fmt.Println(classifyGrade(55)) // Failed
23}

Fallthrough: forces execution of the next case without evaluating its condition.

go
1package main
2
3import "fmt"
4
5func main() {
6    number := 5
7
8    switch {
9    case number > 0:
10        fmt.Println("Positive")
11        fallthrough
12    case number > -10:
13        fmt.Println("Greater than -10")
14    }
15    // Prints: Positive
16    //         Greater than -10
17}
Warning: Use fallthrough with caution. It is uncommon in Go and can make code difficult to understand. Most Go programmers prefer to avoid it unless strictly necessary.

The for loop: the only loop in Go

Go has only one type of loop: the for loop. But it is versatile enough to cover all the cases that other languages handle with while, do-while, or foreach.

Classic for with initialization, condition, and increment:

go
1package main
2
3import "fmt"
4
5func main() {
6    // Classic for (like C/Java)
7    for i := 0; i < 5; i++ {
8        fmt.Println("Iteration:", i)
9    }
10}

While-style for — condition only:

go
1package main
2
3import "fmt"
4
5func main() {
6    counter := 10
7
8    for counter > 0 {
9        fmt.Println("Countdown:", counter)
10        counter--
11    }
12    fmt.Println("Liftoff!")
13}

Infinite for — no condition (exit with break):

go
1package main
2
3import (
4    "bufio"
5    "fmt"
6    "os"
7    "strings"
8)
9
10func main() {
11    scanner := bufio.NewScanner(os.Stdin)
12
13    for {
14        fmt.Print("Type something (or 'quit' to exit): ")
15        scanner.Scan()
16        text := scanner.Text()
17
18        if strings.ToLower(text) == "quit" {
19            fmt.Println("Goodbye!")
20            break
21        }
22        fmt.Println("You typed:", text)
23    }
24}

For range — iterate over slices, arrays, maps, and strings:

go
1package main
2
3import "fmt"
4
5func main() {
6    fruits := []string{"apple", "banana", "orange", "grape"}
7
8    // With index and value
9    for index, fruit := range fruits {
10        fmt.Printf("Position %d: %s
11", index, fruit)
12    }
13
14    // Value only (ignore index with _)
15    for _, fruit := range fruits {
16        fmt.Println("Fruit:", fruit)
17    }
18
19    // Index only
20    for i := range fruits {
21        fmt.Println("Index:", i)
22    }
23}

Break, continue, and labels

Go offers break and continue to control flow within loops, just like other languages. But it also supports labels to control nested loops.

go
1package main
2
3import "fmt"
4
5func main() {
6    // continue: skip to next iteration
7    for i := 1; i <= 10; i++ {
8        if i%2 == 0 {
9            continue // Skip even numbers
10        }
11        fmt.Println("Odd:", i)
12    }
13
14    fmt.Println("---")
15
16    // break: exit the loop
17    for i := 1; i <= 100; i++ {
18        if i > 5 {
19            break
20        }
21        fmt.Println("Number:", i)
22    }
23}

Labels are useful when you have nested loops and want to break out of the outer loop from inside the inner one:

go
1package main
2
3import "fmt"
4
5func main() {
6outer:
7    for i := 0; i < 5; i++ {
8        for j := 0; j < 5; j++ {
9            if i+j == 4 {
10                fmt.Printf("Exiting at i=%d, j=%d
11", i, j)
12                break outer // Breaks out of the outer loop
13            }
14            fmt.Printf("i=%d, j=%d
15", i, j)
16        }
17    }
18    fmt.Println("End of program")
19}
Note: Labels in Go must be placed right before the for statement they refer to. They are mainly used to break out of nested loops, something that in other languages requires auxiliary control variables or separate functions.

Practical examples

Let us combine everything we learned in three classic programming examples.

FizzBuzz: print numbers from 1 to 30. If the number is divisible by 3, print "Fizz". If divisible by 5, print "Buzz". If divisible by both, print "FizzBuzz".

go
1package main
2
3import "fmt"
4
5func main() {
6    for i := 1; i <= 30; i++ {
7        switch {
8        case i%15 == 0:
9            fmt.Println("FizzBuzz")
10        case i%3 == 0:
11            fmt.Println("Fizz")
12        case i%5 == 0:
13            fmt.Println("Buzz")
14        default:
15            fmt.Println(i)
16        }
17    }
18}

Sum of elements in a slice:

go
1package main
2
3import "fmt"
4
5func main() {
6    numbers := []int{10, 25, 33, 47, 52, 68, 71, 89}
7    sum := 0
8    largest := numbers[0]
9
10    for _, n := range numbers {
11        sum += n
12        if n > largest {
13            largest = n
14        }
15    }
16
17    average := float64(sum) / float64(len(numbers))
18    fmt.Printf("Sum: %d
19", sum)
20    fmt.Printf("Average: %.2f
21", average)
22    fmt.Printf("Largest: %d
23", largest)
24}

Number guessing game:

go
1package main
2
3import (
4    "fmt"
5    "math/rand"
6    "time"
7)
8
9func main() {
10    rand.Seed(time.Now().UnixNano())
11    secret := rand.Intn(100) + 1
12    attempts := 0
13
14    fmt.Println("=== Guess the Number ===")
15    fmt.Println("I picked a number between 1 and 100.")
16
17    for {
18        var guess int
19        fmt.Print("Your guess: ")
20        _, err := fmt.Scan(&guess)
21        if err != nil {
22            fmt.Println("Please enter a valid number.")
23            continue
24        }
25
26        attempts++
27
28        switch {
29        case guess < secret:
30            fmt.Println("Too low. Try again.")
31        case guess > secret:
32            fmt.Println("Too high. Try again.")
33        default:
34            fmt.Printf("Correct! The number was %d
35", secret)
36            fmt.Printf("You got it in %d attempts.
37", attempts)
38
39            switch {
40            case attempts <= 5:
41                fmt.Println("Amazing, you are a genius!")
42            case attempts <= 10:
43                fmt.Println("Well done!")
44            default:
45                fmt.Println("You got it, but you can do better.")
46            }
47            return
48        }
49    }
50}
Tip: This example combines an infinite for loop, conditionless switch, continue for input error handling, and return to end the function when the user guesses correctly. It is a great exercise to practice everything covered in this article.

Summary and next article

In this article you learned the tools to control execution flow in Go:

  • if/else with init statement: make decisions with narrowly scoped variables
  • Comparison and logical operators: build complex conditions with ==, !=, &&, ||
  • switch: elegant multi-way selection, with or without expression, and optional fallthrough
  • Classic for: the only loop in Go, with initialization, condition, and increment
  • While-style for: condition only, to repeat while something is true
  • Infinite for: exit with break or return
  • For range: iterate over slices, arrays, maps, and strings idiomatically
  • break, continue, and labels: control execution in simple and nested loops

In the next article (Part 4) we will learn about functions and error handling: how to organize your code into reusable functions, take advantage of multiple return values in Go, and master the if err != nil pattern.

Share:
CV

Cristhian Villegas

Software Engineer specializing in Java, Spring Boot, Angular & AWS. Building scalable distributed systems with clean architecture.

Comments

Sign in to leave a comment

No comments yet. Be the first!

Related Articles