Skip to content

Go Control Flow — if else for switch and defer Explained

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about Go Control Flow. We cover key concepts, practical examples, and best practices to help you master this topic.

Go control flow includes if/else with initialization statements, for as the only loop keyword (no while/until), switch with expressionless and type-switch forms, and defer for guaranteed cleanup.

What You'll Learn

  • If/else with initialization statements
  • For loops in all forms
  • Switch statements with multiple cases
  • Defer for cleanup operations

Why It Matters

Go's control flow is minimal but expressive. Docker uses for loops for container operations. Kubernetes controllers use switch statements for resource reconciliation. Doda Browser uses defer for resource cleanup. Mastering these constructs lets you write idiomatic Go.

Real-World Use

An HTTP handler checks for errors with if, iterates over data with for, handles different content types with switch, and defers response writing. A CLI tool loops over arguments, parses flags, and handles subcommands with switch.

flowchart LR
    A["Control Flow"] --> B["if/else"]
    B --> C["for loops"]
    C --> D["switch"]
    D --> E["defer"]
    A:::current --> B
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b

If/Else

package main

import "fmt"

func main() {
    age := 18

    if age >= 18 {
        fmt.Println("Adult")
    } else {
        fmt.Println("Minor")
    }
}

If with Initialization

if score := 85; score >= 90 {
    fmt.Println("Grade: A")
} else if score >= 80 {
    fmt.Println("Grade: B")
} else if score >= 70 {
    fmt.Println("Grade: C")
} else {
    fmt.Println("Grade: D")
}

The score variable is scoped to the if/else block — it doesn't leak outside.

For Loop

Go has only for (no while, until, or do-while). For handles all looping needs.

Standard Three-Component For

for i := 0; i < 5; i++ {
    fmt.Println(i)
}
// 0 1 2 3 4

While-Style For

count := 0
for count < 5 {
    fmt.Println(count)
    count++
}
// 0 1 2 3 4

Infinite Loop

count := 0
for {
    fmt.Println(count)
    count++
    if count >= 5 {
        break
    }
}

Range Loop

// Slice/Array
fruits := []string{"apple", "banana", "cherry"}
for index, fruit := range fruits {
    fmt.Printf("%d: %s\n", index, fruit)
}
// 0: apple
// 1: banana
// 2: cherry

// Map
ages := map[string]int{"Alice": 25, "Bob": 30}
for name, age := range ages {
    fmt.Printf("%s is %d\n", name, age)
}

// String (iterates runes)
for i, r := range "Hello" {
    fmt.Printf("%d: %c\n", i, r)
}

// Channel
ch := make(chan int)
go func() {
    for i := 0; i < 3; i++ {
        ch <- i
    }
    close(ch)
}()
for n := range ch {
    fmt.Println(n)
}

Break and Continue

for i := 0; i < 10; i++ {
    if i == 3 {
        continue  // Skip 3
    }
    if i == 8 {
        break     // Stop at 8
    }
    fmt.Println(i)
}
// 0 1 2 4 5 6 7

Switch Statement

Go's switch is more flexible than many languages:

Basic Switch

day := "Monday"

switch day {
case "Monday", "Tuesday":
    fmt.Println("Weekday start")
case "Friday":
    fmt.Println("Almost weekend")
case "Saturday", "Sunday":
    fmt.Println("Weekend!")
default:
    fmt.Println("Midweek")
}

No break needed — Go automatically breaks after each case.

Expressionless Switch

score := 85

switch {
case score >= 90:
    fmt.Println("A")
case score >= 80:
    fmt.Println("B")
case score >= 70:
    fmt.Println("C")
default:
    fmt.Println("D")
}

Switch with Initialization

switch result := calculate(); {
case result > 100:
    fmt.Println("High")
case result > 50:
    fmt.Println("Medium")
default:
    fmt.Println("Low")
}

Type Switch

func describe(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Integer: %d\n", v)
    case string:
        fmt.Printf("String: %s\n", v)
    case bool:
        fmt.Printf("Boolean: %v\n", v)
    default:
        fmt.Printf("Unknown type: %T\n", v)
    }
}

Defer

defer schedules a function call to run after the surrounding function returns:

func main() {
    defer fmt.Println("World")
    fmt.Print("Hello ")
}
// Hello World

Deferred Cleanup

func readFile(path string) error {
    f, err := os.Open(path)
    if err != nil {
        return err
    }
    defer f.Close()  // Runs when function returns

    // Process file...
    return nil
}

Multiple Defers (LIFO Order)

func main() {
    defer fmt.Println("First deferred")
    defer fmt.Println("Second deferred")
    defer fmt.Println("Third deferred")
    fmt.Println("Main body")
}
// Main body
// Third deferred
// Second deferred
// First deferred

Common Mistakes

1. Curly Brace on Wrong Line

// Wrong — syntax error
if x > 0
{
    fmt.Println("Positive")
}

// Right
if x > 0 {
    fmt.Println("Positive")
}

2. Using Break in Switch When You Want Fallthrough

switch x {
case 1:
    fmt.Println("One")
    // Implicit break — doesn't fall through
case 2:
    fmt.Println("Two")
}
// Add explicit 'fallthrough' to continue

3. Forgetting That Range Copies Values

for _, v := range slice {
    v = 0  // Modifies copy, not original
}
// Use index: for i := range slice { slice[i] = 0 }

4. Using := with if Initialization and Then Confusing Scope

if x := risky(); x > 0 {
    fmt.Println(x)  // This x is from the if
}
// fmt.Println(x)  // Error: x is undefined here

5. Defer Inside a Loop

for _, file := range files {
    f, _ := os.Open(file)
    defer f.Close()  // Deferred defers don't run until function returns!
}
// Move defer inside a helper function or close explicitly

6. Modifying Loop Variables in Goroutines

for i := 0; i < 5; i++ {
    go func() {
        fmt.Println(i)  // All goroutines see same i!
    }()
}
// Fix: pass i as argument: go func(n int) { fmt.Println(n) }(i)

Practice Questions

1. How do you write a while loop in Go?

Use for with just the condition: for count < 10 { ... }. Go has no while keyword — for handles all looping patterns.

2. What does defer do?

defer schedules a function call to execute immediately before the surrounding function returns. It's typically used for cleanup (closing files, releasing locks).

3. How does Go's switch differ from other languages?

Go's switch doesn't require break statements — it automatically exits after each case. Cases can have multiple values. Switch can be expressionless for if-else chains. Type switches work on interface types.

4. What is the range keyword used for?

range iterates over slices, arrays, maps, strings, and channels. It returns index/element pairs for slices, key/value pairs for maps, and rune/byte pairs for strings.

Challenge: Write a Go program that uses for, switch, defer, and if-with-initialization to Process command-line arguments as a simple calculator.

Solution
package main

import (
    "fmt"
    "os"
    "strconv"
)

func main() {
    if len(os.Args) < 4 {
        fmt.Println("Usage: calc <a> <op> <b>")
        return
    }

    a, _ := strconv.ParseFloat(os.Args[1], 64)
    op := os.Args[2]
    b, _ := strconv.ParseFloat(os.Args[3], 64)

    var result float64
    switch op {
    case "+":
        result = a + b
    case "-":
        result = a - b
    case "*":
        result = a * b
    case "/":
        if b == 0 {
            fmt.Println("Cannot divide by zero")
            return
        }
        result = a / b
    default:
        fmt.Println("Unknown operator:", op)
        return
    }

    defer fmt.Println("Calculation complete")
    fmt.Printf("%.2f %s %.2f = %.2f\n", a, op, b, result)
}

FAQ

{{< faq question="Is there a while loop in Go?" >}} No. Go has only for. Use for condition { } for while-style loops and for { } for infinite loops. Go's designers felt one loop keyword with multiple forms was simpler. {{< /faq >}}

{{< faq question="Does go switch need break statements?" >} No. Go automatically exits a case after execution. Use fallthrough if you want to continue to the next case (rarely needed). {{< /faq >}}

{{< faq question="What happens if I defer multiple functions?" >} They execute in LIFO (last-in, first-out) order. The last deferred call runs first when the function returns. {{< /faq >}}

{{< faq question="Can I use break in a switch case?" >} It's not needed — Go breaks automatically. break in a switch is only useful when you need to break out of an enclosing for loop from within a switch. {{< /faq >}}

{{< faq question="What is the difference between range and a regular for loop?" >} range is syntactic sugar for iterating over collections. Internally, the compiler generates for loop code. Use range for simplicity and readability. {{< /faq >}}

Try It Yourself

package main

import (
    "fmt"
)

func main() {
    // If with init
    if n := 10; n%2 == 0 {
        fmt.Println(n, "is even")
    }

    // For as while
    sum := 0
    for sum < 10 {
        sum += 3
        fmt.Println("Sum:", sum)
    }

    // Switch
    lang := "Go"
    switch lang {
    case "Go", "Rust":
        fmt.Println("Systems language")
    case "Python", "Ruby":
        fmt.Println("Scripting language")
    default:
        fmt.Println("Unknown")
    }

    // Defer
    defer fmt.Println("Cleanup done")
    fmt.Println("Processing...")
}

Expected output:

10 is even
Sum: 3
Sum: 6
Sum: 9
Sum: 12
Systems language
Processing...
Cleanup done

What's Next

Now that you understand control flow, learn about functions in Go.

Topic Description Link
Go Functions func, returns, multiple returns {{< ref "06-functions" >}}
Go Arrays & Slices Fixed arrays, slice, append, make {{< ref "07-arrays-slices" >}}
Rust Control Flow Compare Rust control flow Rust

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro

Home Browse Go