Control Flow

Why This Matters - The Foundation of Program Logic

Control flow determines how your program makes decisions and repeats actions. It's the intelligence behind every application - from simple calculators to complex AI systems. Understanding control flow is fundamental because:

  • Decision Making: Programs constantly choose between different paths based on conditions
  • Efficiency: The right control structures make code faster and more readable
  • Logic Building: Complex behaviors are built from simple control flow patterns
  • Problem Solving: Most algorithms are fundamentally about controlling execution flow

Real-World Impact: Every application you use relies on control flow - web servers route requests, games handle user input, data pipelines process information, and APIs validate requests. Mastering control flow means you can build any kind of program.

Learning Objectives

By the end of this article, you will:

  • ✅ Understand Go's simplified control flow philosophy
  • ✅ Master conditional logic with if statements and initialization
  • ✅ Use for loops in all their forms
  • ✅ Iterate over collections efficiently with range
  • ✅ Write clean multi-way branching with switch statements
  • ✅ Use defer for reliable resource cleanup
  • ✅ Apply control flow patterns to solve real-world problems
  • ✅ Avoid common pitfalls that cause bugs and performance issues

Go's Control Flow Philosophy - Less is More

Go deliberately simplifies control flow by providing fewer, more consistent constructs:

 1// ❌ Other languages have multiple ways to loop:
 2while { }           // While loop
 3do { } while;          // Do-while loop
 4for { }     // Classic for loop
 5foreach { }  // For-each loop
 6
 7// ✅ Go: One loop that does everything efficiently
 8for condition { }                 // While-style loop
 9for i := 0; i < n; i++ { }    // Classic loop
10for _, item := range collection { } // Range iteration
11for { }                          // Infinite loop

Why Go Chose Simplicity:

  1. Reduced Cognitive Load: One way to do things = easier to read and write
  2. Fewer Bugs: Less syntax variations = fewer places to make mistakes
  3. Consistency: Same patterns everywhere = predictable code
  4. Performance: Go's for is highly optimized for all use cases

Key Design Decisions:

  • No parentheses around conditions
  • No ternary operator
  • No while, do-while
  • No automatic fallthrough in switch

💡 Key Insight: Go's constraints are liberating - fewer choices mean you spend less time deciding and more time building.

Core Concepts - Understanding Control Flow Building Blocks

Conditional Logic: Making Decisions

Conditional logic is about choosing between different paths based on conditions. Think of it as your program's decision-making ability.

Real-World Example: A thermostat system

  • If temperature > 75°F: turn on air conditioning
  • If temperature < 65°F: turn on heating
  • Otherwise: maintain current state
1// Simple conditional example
2temperature := 72
3if temperature > 75 {
4    fmt.Println("Turning on AC")
5} else if temperature < 65 {
6    fmt.Println("Turning on heat")
7} else {
8    fmt.Println("Maintaining current temperature")
9}

The Anatomy of Conditional Logic:

Conditional statements evaluate boolean expressions and execute different code paths based on the result. In Go, every condition must evaluate to a boolean value - there's no concept of "truthy" or "falsy" values like in some other languages.

 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    // Boolean expressions in Go
 8    age := 25
 9    hasLicense := true
10
11    // Simple boolean evaluation
12    if age >= 18 {
13        fmt.Println("Adult")
14    }
15
16    // Compound conditions with logical operators
17    if age >= 18 && hasLicense {
18        fmt.Println("Can drive")
19    }
20
21    // Negation
22    if !hasLicense {
23        fmt.Println("Cannot drive - no license")
24    }
25
26    // Multiple else-if chains
27    if age < 13 {
28        fmt.Println("Child")
29    } else if age < 20 {
30        fmt.Println("Teenager")
31    } else if age < 65 {
32        fmt.Println("Adult")
33    } else {
34        fmt.Println("Senior")
35    }
36}

Understanding Boolean Operators:

Go provides three primary boolean operators for combining conditions:

  • && (AND): Both conditions must be true
  • || (OR): At least one condition must be true
  • ! (NOT): Inverts the boolean value
 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    // AND operator - both must be true
 8    fmt.Println("AND truth table:")
 9    fmt.Printf("true && true = %t\n", true && true)
10    fmt.Printf("true && false = %t\n", true && false)
11    fmt.Printf("false && true = %t\n", false && true)
12    fmt.Printf("false && false = %t\n", false && false)
13
14    // OR operator - at least one must be true
15    fmt.Println("\nOR truth table:")
16    fmt.Printf("true || true = %t\n", true || true)
17    fmt.Printf("true || false = %t\n", true || false)
18    fmt.Printf("false || true = %t\n", false || true)
19    fmt.Printf("false || false = %t\n", false || false)
20
21    // NOT operator - inverts the value
22    fmt.Println("\nNOT operator:")
23    fmt.Printf("!true = %t\n", !true)
24    fmt.Printf("!false = %t\n", !false)
25
26    // Short-circuit evaluation
27    fmt.Println("\nShort-circuit behavior:")
28    x := 5
29    // The second condition is never evaluated if first is false
30    if x > 10 && expensiveCheck() {
31        fmt.Println("Both conditions true")
32    } else {
33        fmt.Println("First condition was false, second never checked")
34    }
35}
36
37func expensiveCheck() bool {
38    fmt.Println("  Expensive check called!")
39    return true
40}

Repetition: Doing Things Multiple Times

Loops allow programs to repeat actions efficiently. Instead of writing the same code 100 times, you write it once and tell Go to repeat it.

Real-World Example: Processing customer orders

  • For each customer in line: take their order
  • For each item in cart: scan and price it
  • For each payment method: validate and process
1// Simple loop example
2customers := []string{"Alice", "Bob", "Charlie"}
3for _, customer := range customers {
4    fmt.Printf("Processing order for %s\n", customer)
5}

Loop Mechanics and Control:

Go's for loop is remarkably versatile because it's designed to handle every looping scenario. Understanding how loops work internally helps you write more efficient code.

 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    // Loop initialization, condition, and post statement
 8    fmt.Println("Classic for loop anatomy:")
 9    for i := 0; i < 5; i++ {
10        fmt.Printf("Iteration %d\n", i)
11    }
12
13    // The parts of a for loop:
14    // 1. Init statement: i := 0 (runs once before loop starts)
15    // 2. Condition: i < 5 (checked before each iteration)
16    // 3. Post statement: i++ (runs after each iteration)
17
18    // While-style loop (only condition)
19    fmt.Println("\nWhile-style loop:")
20    count := 0
21    for count < 3 {
22        fmt.Printf("Count: %d\n", count)
23        count++
24    }
25
26    // Infinite loop with manual break
27    fmt.Println("\nInfinite loop with break:")
28    n := 0
29    for {
30        if n >= 3 {
31            break  // Exit the loop
32        }
33        fmt.Printf("N: %d\n", n)
34        n++
35    }
36
37    // Continue statement - skip to next iteration
38    fmt.Println("\nUsing continue:")
39    for i := 0; i < 5; i++ {
40        if i == 2 {
41            continue  // Skip iteration when i is 2
42        }
43        fmt.Printf("Processing %d\n", i)
44    }
45}

Nested Loops and Complexity:

Nested loops are powerful but require careful consideration of their time complexity. Each nested loop multiplies the number of iterations.

 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    // Single loop: O(n) - linear time
 8    fmt.Println("Single loop (5 iterations):")
 9    iterations := 0
10    for i := 0; i < 5; i++ {
11        iterations++
12    }
13    fmt.Printf("Total iterations: %d\n\n", iterations)
14
15    // Nested loop: O(n²) - quadratic time
16    fmt.Println("Nested loop (5x5 = 25 iterations):")
17    iterations = 0
18    for i := 0; i < 5; i++ {
19        for j := 0; j < 5; j++ {
20            iterations++
21            if j < 3 {  // Print first few to show pattern
22                fmt.Printf("[%d,%d] ", i, j)
23            }
24        }
25        if i < 3 {
26            fmt.Println()
27        }
28    }
29    fmt.Printf("\nTotal iterations: %d\n\n", iterations)
30
31    // Triple nested loop: O(n³) - cubic time
32    fmt.Println("Triple nested loop (3x3x3 = 27 iterations):")
33    iterations = 0
34    for i := 0; i < 3; i++ {
35        for j := 0; j < 3; j++ {
36            for k := 0; k < 3; k++ {
37                iterations++
38            }
39        }
40    }
41    fmt.Printf("Total iterations: %d\n", iterations)
42
43    // Practical example: Matrix multiplication
44    fmt.Println("\nMatrix multiplication pattern:")
45    matrixA := [][]int{
46        {1, 2, 3},
47        {4, 5, 6},
48    }
49    matrixB := [][]int{
50        {7, 8},
51        {9, 10},
52        {11, 12},
53    }
54
55    // Result will be 2x2
56    result := make([][]int, len(matrixA))
57    for i := range result {
58        result[i] = make([]int, len(matrixB[0]))
59    }
60
61    // Three nested loops: i for rows, j for cols, k for multiplication
62    for i := 0; i < len(matrixA); i++ {
63        for j := 0; j < len(matrixB[0]); j++ {
64            sum := 0
65            for k := 0; k < len(matrixB); k++ {
66                sum += matrixA[i][k] * matrixB[k][j]
67            }
68            result[i][j] = sum
69        }
70    }
71
72    fmt.Println("Result matrix:")
73    for _, row := range result {
74        fmt.Println(row)
75    }
76}

Selection: Choosing Between Multiple Options

Switch statements provide clean ways to choose between many options, replacing long chains of if-else statements.

Real-World Example: Menu-driven application

  • If user presses '1': show account balance
  • If user presses '2': show transaction history
  • If user presses '3': transfer funds
  • If user presses '4': exit

Switch Statement Power:

Go's switch statements are more powerful than in most languages. They don't require break statements, can switch on any comparable type, and can even have no expression at all.

 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "time"
 7)
 8
 9func main() {
10    // Traditional switch on value
11    day := time.Now().Weekday()
12    fmt.Printf("Today is %v\n", day)
13
14    switch day {
15    case time.Monday:
16        fmt.Println("Start of work week")
17    case time.Friday:
18        fmt.Println("End of work week")
19    case time.Saturday, time.Sunday:
20        fmt.Println("Weekend!")
21    default:
22        fmt.Println("Midweek day")
23    }
24
25    // Switch without expression (replaces if-else chains)
26    hour := time.Now().Hour()
27    fmt.Printf("\nCurrent hour: %d\n", hour)
28
29    switch {
30    case hour < 6:
31        fmt.Println("Early morning")
32    case hour < 12:
33        fmt.Println("Morning")
34    case hour < 17:
35        fmt.Println("Afternoon")
36    case hour < 21:
37        fmt.Println("Evening")
38    default:
39        fmt.Println("Night")
40    }
41
42    // Type switch
43    var value interface{} = 42
44
45    switch v := value.(type) {
46    case int:
47        fmt.Printf("\nInteger: %d\n", v)
48    case string:
49        fmt.Printf("\nString: %s\n", v)
50    case bool:
51        fmt.Printf("\nBoolean: %t\n", v)
52    default:
53        fmt.Printf("\nUnknown type: %T\n", v)
54    }
55
56    // Fallthrough (explicit, unlike C)
57    score := 85
58    fmt.Printf("\nScore: %d\n", score)
59
60    switch {
61    case score >= 90:
62        fmt.Println("Excellent")
63    case score >= 80:
64        fmt.Println("Good")
65        fallthrough  // Explicitly continue to next case
66    case score >= 70:
67        fmt.Println("Passed with credit")
68    default:
69        fmt.Println("Needs improvement")
70    }
71}

Practical Examples - Control Flow in Action

If Statements with Initialization

Go's if can initialize variables right in the condition, keeping scope limited and code clean.

 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    // Initialize variable and check condition in one statement
 8    if user := getCurrentUser(); user != nil {
 9        fmt.Printf("Welcome back, %s!\n", user.Name)
10    } else {
11        fmt.Println("Please log in to continue")
12    }
13}
14
15func getCurrentUser() *User {
16    // Simulate database lookup
17    return &User{Name: "Alice", ID: 123}
18}
19
20type User struct {
21    Name string
22    ID   int
23}

Why This Pattern Matters:

  • Scope Limitation: Variable only exists where needed
  • Error Handling: Perfect for checking function results inline
  • Clean Code: No temporary variables cluttering outer scope

Advanced If-Statement Patterns:

 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "math/rand"
 7    "time"
 8)
 9
10func main() {
11    rand.Seed(time.Now().UnixNano())
12
13    // Pattern 1: Error checking with initialization
14    if err := validateData(rand.Intn(100)); err != nil {
15        fmt.Printf("Validation failed: %v\n", err)
16    } else {
17        fmt.Println("Data is valid")
18    }
19
20    // Pattern 2: Multiple return values
21    if value, ok := tryGetValue(); ok {
22        fmt.Printf("Retrieved value: %d\n", value)
23    } else {
24        fmt.Println("Value not available")
25    }
26
27    // Pattern 3: Complex condition with initialization
28    if result := computeExpensiveValue(); result > 50 && result < 100 {
29        fmt.Printf("Result %d is in acceptable range\n", result)
30    } else {
31        fmt.Printf("Result %d is out of range\n", result)
32    }
33
34    // Pattern 4: Nested if with scope management
35    if config := loadConfig(); config != nil {
36        if config.Enabled {
37            fmt.Println("Feature is enabled")
38            if config.Level > 5 {
39                fmt.Println("Advanced features available")
40            }
41        }
42    }
43}
44
45func validateData(value int) error {
46    if value < 0 || value > 100 {
47        return fmt.Errorf("value %d out of range", value)
48    }
49    return nil
50}
51
52func tryGetValue() (int, bool) {
53    if rand.Float64() > 0.5 {
54        return rand.Intn(100), true
55    }
56    return 0, false
57}
58
59func computeExpensiveValue() int {
60    // Simulate expensive computation
61    return rand.Intn(150)
62}
63
64type Config struct {
65    Enabled bool
66    Level   int
67}
68
69func loadConfig() *Config {
70    return &Config{Enabled: true, Level: 7}
71}

For Loops: All Forms Covered

Go's single for loop handles every looping scenario efficiently.

Form 1: Classic C-style Loop

 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    // Count from 0 to 9
 8    for i := 0; i < 10; i++ {
 9        fmt.Printf("Count: %d\n", i)
10    }
11}

When to Use: When you know exact number of iterations and need index access.

Form 2: While-style Loop

 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    // Read until user enters "quit"
 8    input := ""
 9    for input != "quit" {
10        fmt.Print("Enter command: ")
11        fmt.Scanln(&input)
12        fmt.Printf("You entered: %s\n", input)
13    }
14    fmt.Println("Goodbye!")
15}

When to Use: When you don't know how many iterations but know when to stop.

Form 3: Infinite Loop with Explicit Exit

 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "time"
 7    "math/rand"
 8)
 9
10func main() {
11    // Game loop - run until player wins or quits
12    score := 0
13    for {
14        fmt.Printf("Current score: %d\n", score)
15
16        // Simulate game action
17        action := rand.Intn(3)
18        switch action {
19        case 0:
20            points := rand.Intn(10) + 1
21            score += points
22            fmt.Printf("You gained %d points!\n", points)
23        case 1:
24            fmt.Println("Nothing happened this turn")
25        case 2:
26            if score > 0 {
27                loss := rand.Intn(score) + 1
28                score -= loss
29                fmt.Printf("You lost %d points!\n", loss)
30            }
31        }
32
33        if score >= 50 {
34            fmt.Printf("Congratulations! You reached %d points and won!\n", score)
35            break
36        }
37
38        time.Sleep(500 * time.Millisecond)
39    }
40}

When to Use: For continuous processes like servers, game loops, or monitoring systems.

Advanced Loop Patterns:

 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    // Pattern 1: Loop with multiple variables
 8    fmt.Println("Fibonacci sequence:")
 9    for a, b := 0, 1; a < 100; a, b = b, a+b {
10        fmt.Printf("%d ", a)
11    }
12    fmt.Println()
13
14    // Pattern 2: Labeled loops for breaking nested loops
15    fmt.Println("\nBreaking nested loops:")
16outer:
17    for i := 0; i < 3; i++ {
18        for j := 0; j < 3; j++ {
19            fmt.Printf("[%d,%d] ", i, j)
20            if i == 1 && j == 1 {
21                fmt.Println("\nBreaking out of both loops")
22                break outer  // Breaks from outer loop
23            }
24        }
25    }
26
27    // Pattern 3: Loop with continue to specific label
28    fmt.Println("\nContinue with label:")
29outer2:
30    for i := 0; i < 3; i++ {
31        for j := 0; j < 3; j++ {
32            if j == 1 {
33                continue outer2  // Continue outer loop
34            }
35            fmt.Printf("[%d,%d] ", i, j)
36        }
37    }
38    fmt.Println()
39
40    // Pattern 4: Post-condition loop simulation
41    fmt.Println("\nDo-while style loop:")
42    counter := 0
43    for {
44        fmt.Printf("Counter: %d\n", counter)
45        counter++
46
47        if counter >= 3 {
48            break
49        }
50    }
51}

Range Iteration: Collection Processing

range is Go's elegant solution for iterating over collections safely and efficiently.

 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    // Process different collection types
 8    fruits := []string{"apple", "banana", "cherry"}
 9
10    fmt.Println("Processing fruits:")
11    for i, fruit := range fruits {
12        fmt.Printf("%d: %s\n", i+1, fruit)
13    }
14
15    // Map iteration
16    prices := map[string]float64{
17        "apple":  0.99,
18        "banana": 0.59,
19        "cherry": 2.99,
20    }
21
22    fmt.Println("\nPrice list:")
23    for fruit, price := range prices {
24        fmt.Printf("%s: $%.2f\n", fruit, price)
25    }
26
27    // String iteration
28    word := "hello"
29    fmt.Println("\nString characters:")
30    for i, char := range word {
31        fmt.Printf("%d: %c\n", i, char)
32    }
33}

Range Deep Dive:

 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    // Range on slices - returns index and value
 8    numbers := []int{10, 20, 30, 40, 50}
 9
10    fmt.Println("Full range:")
11    for i, v := range numbers {
12        fmt.Printf("Index %d: Value %d\n", i, v)
13    }
14
15    // Ignore index with blank identifier
16    fmt.Println("\nValues only:")
17    for _, v := range numbers {
18        fmt.Printf("%d ", v)
19    }
20    fmt.Println()
21
22    // Ignore value (just iterate indices)
23    fmt.Println("\nIndices only:")
24    for i := range numbers {
25        fmt.Printf("%d ", i)
26    }
27    fmt.Println()
28
29    // Range on maps - order is random
30    fmt.Println("\nMap iteration (random order):")
31    m := map[string]int{"a": 1, "b": 2, "c": 3}
32    for key, value := range m {
33        fmt.Printf("%s=%d ", key, value)
34    }
35    fmt.Println()
36
37    // Range on strings - yields runes (Unicode code points)
38    fmt.Println("\nString iteration (runes):")
39    text := "Hello, 世界"
40    for i, r := range text {
41        fmt.Printf("Byte %d: '%c' (U+%04X)\n", i, r, r)
42    }
43
44    // Range on channels
45    fmt.Println("\nChannel iteration:")
46    ch := make(chan int, 3)
47    ch <- 1
48    ch <- 2
49    ch <- 3
50    close(ch)
51
52    for value := range ch {
53        fmt.Printf("Received: %d\n", value)
54    }
55}

Switch Statements: Clean Multi-way Branching

Switch statements provide cleaner alternatives to long if-else chains.

 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    // Grade calculator using switch
 8    score := 85
 9
10    switch {
11    case score >= 90:
12        fmt.Println("Grade: A - Excellent!")
13    case score >= 80:
14        fmt.Println("Grade: B - Good job!")
15    case score >= 70:
16        fmt.Println("Grade: C - You passed")
17    case score >= 60:
18        fmt.Println("Grade: D - Need improvement")
19    default:
20        fmt.Println("Grade: F - See me after class")
21    }
22
23    // Menu processor
24    fmt.Println("\nMenu:")
25    fmt.Println("1. Check balance")
26    fmt.Println("2. Deposit money")
27    fmt.Println("3. Withdraw money")
28    fmt.Println("4. Exit")
29
30    var choice int
31    fmt.Print("Enter your choice: ")
32    fmt.Scanln(&choice)
33
34    switch choice {
35    case 1:
36        checkBalance()
37    case 2:
38        depositMoney()
39    case 3:
40        withdrawMoney()
41    case 4:
42        fmt.Println("Thank you for banking with us!")
43    default:
44        fmt.Println("Invalid choice. Please try again.")
45    }
46}
47
48func checkBalance() {
49    fmt.Println("Your balance is $1,234.56")
50}
51
52func depositMoney() {
53    fmt.Println("Deposit feature coming soon!")
54}
55
56func withdrawMoney() {
57    fmt.Println("Withdrawal feature coming soon!")
58}

Defer: Guaranteed Cleanup

The defer statement ensures code runs when a function exits, regardless of how it exits.

 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    demonstrateDefer()
 8    demonstrateMultipleDefers()
 9    demonstrateDeferWithPanic()
10}
11
12func demonstrateDefer() {
13    fmt.Println("\n=== Basic Defer ===")
14    defer fmt.Println("This runs last")
15    fmt.Println("This runs first")
16    fmt.Println("This runs second")
17}
18
19func demonstrateMultipleDefers() {
20    fmt.Println("\n=== Multiple Defers (LIFO) ===")
21    defer fmt.Println("Defer 1 (runs third)")
22    defer fmt.Println("Defer 2 (runs second)")
23    defer fmt.Println("Defer 3 (runs first)")
24    fmt.Println("Normal execution")
25}
26
27func demonstrateDeferWithPanic() {
28    fmt.Println("\n=== Defer with Panic Recovery ===")
29
30    defer func() {
31        if r := recover(); r != nil {
32            fmt.Printf("Recovered from panic: %v\n", r)
33        }
34        fmt.Println("Cleanup completed even after panic")
35    }()
36
37    fmt.Println("About to panic...")
38    // Uncommenting will demonstrate panic recovery
39    // panic("Something went wrong!")
40    fmt.Println("Continuing normally")
41}

Common Patterns and Pitfalls

Pattern 1: Input Validation Loop

 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "strings"
 7)
 8
 9func main() {
10    // Keep asking until valid input received
11    for {
12        fmt.Print("Enter your age: ")
13        var age int
14        _, err := fmt.Scanln(&age)
15
16        if err != nil {
17            fmt.Println("Invalid input. Please enter a number.")
18            continue
19        }
20
21        if age >= 18 && age <= 120 {
22            fmt.Printf("Thank you! Your age %d is valid.\n", age)
23            break
24        }
25
26        fmt.Printf("Age %d is outside valid range. Please try again.\n", age)
27    }
28}

Pattern 2: Resource Cleanup with Defer

 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "os"
 7)
 8
 9func main() {
10    processFile("/tmp/example.txt")
11}
12
13func processFile(filename string) {
14    // Open file
15    file, err := os.Open(filename)
16    if err != nil {
17        fmt.Printf("Error opening file: %v\n", err)
18        return
19    }
20
21    // Ensure file is closed even if panic occurs
22    defer func() {
23        fmt.Printf("Closing file: %s\n", filename)
24        file.Close()
25    }()
26
27    // Process file content
28    fmt.Printf("Successfully opened file: %s\n", filename)
29
30    // Simulate potential panic
31    // panic("Something went wrong!")
32
33    fmt.Println("File processing completed")
34}

Common Pitfall 1: Modifying Slice During Range

 1// ❌ WRONG - Modifying slice while iterating
 2func removeEvenWrong(numbers []int) []int {
 3    for i, num := range numbers {
 4        if num%2 == 0 {
 5            // Danger: Modifying slice during iteration!
 6            numbers = append(numbers[:i], numbers[i+1:]...)
 7        }
 8    }
 9    return numbers
10}
11
12// ✅ CORRECT - Create new slice
13func removeEvenCorrect(numbers []int) []int {
14    result := make([]int, 0, len(numbers))
15    for _, num := range numbers {
16        if num%2 != 0 {
17            result = append(result, num)
18        }
19    }
20    return result
21}

Common Pitfall 2: Forgetting Break in Switch

 1// ❌ WRONG - Missing break causes fallthrough
 2func processWeekdayWrong(day int) {
 3    switch day {
 4    case 1, 2, 3, 4, 5:
 5        fmt.Println("Weekday")
 6    case 6, 7:
 7        fmt.Println("Weekend")
 8    }
 9    // No break needed in Go, but this shows the concept
10}
11
12// ✅ CORRECT - Go's switch doesn't fallthrough
13func processWeekdayCorrect(day int) {
14    switch day {
15    case 1, 2, 3, 4, 5:
16        fmt.Println("Weekday")
17    case 6, 7:
18        fmt.Println("Weekend")
19    default:
20        fmt.Println("Invalid day")
21    }
22}

Common Pitfall 3: Off-by-One Errors

 1// ❌ WRONG - Off by one in loop bounds
 2func processArrayWrong() {
 3    arr := [5]int{10, 20, 30, 40, 50}
 4
 5    // Wrong: i <= len(arr) will panic on last iteration
 6    for i := 0; i <= len(arr); i++ {
 7        fmt.Printf("Element %d: %d\n", i, arr[i]) // Panic!
 8    }
 9}
10
11// ✅ CORRECT - Use < not <=
12func processArrayCorrect() {
13    arr := [5]int{10, 20, 30, 40, 50}
14
15    // Correct: i < len(arr)
16    for i := 0; i < len(arr); i++ {
17        fmt.Printf("Element %d: %d\n", i, arr[i])
18    }
19}

Integration and Mastery - Building Real Applications

Example 1: Data Processing Pipeline

 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "math/rand"
 7    "time"
 8)
 9
10type Transaction struct {
11    ID     int
12    Amount float64
13    Valid  bool
14}
15
16func main() {
17    // Generate sample transactions
18    transactions := generateTransactions(1000)
19
20    // Process with different control flow patterns
21    validCount := 0
22    invalidCount := 0
23    totalAmount := 0.0
24
25    for _, tx := range transactions {
26        if tx.Valid {
27            validCount++
28            totalAmount += tx.Amount
29
30            // Categorize by amount
31            switch {
32            case tx.Amount > 1000:
33                fmt.Printf("TX %d: Large transaction $%.2f\n", tx.ID, tx.Amount)
34            case tx.Amount > 100:
35                fmt.Printf("TX %d: Medium transaction $%.2f\n", tx.ID, tx.Amount)
36            default:
37                fmt.Printf("TX %d: Small transaction $%.2f\n", tx.ID, tx.Amount)
38            }
39        } else {
40            invalidCount++
41        }
42    }
43
44    fmt.Printf("\nSummary:\n")
45    fmt.Printf("Valid transactions: %d\n", validCount)
46    fmt.Printf("Invalid transactions: %d\n", invalidCount)
47    fmt.Printf("Total amount: $%.2f\n", totalAmount)
48}
49
50func generateTransactions(count int) []Transaction {
51    transactions := make([]Transaction, count)
52
53    for i := 0; i < count; i++ {
54        transactions[i] = Transaction{
55            ID:     i + 1,
56            Amount: rand.Float64() * 2000,
57            Valid:  rand.Float64() > 0.1, // 90% valid
58        }
59    }
60
61    return transactions
62}

Example 2: Game Loop with State Management

  1// run
  2package main
  3
  4import (
  5    "fmt"
  6    "time"
  7    "math/rand"
  8)
  9
 10type GameState int
 11
 12const (
 13    StateMenu GameState = iota
 14    StatePlaying
 15    StatePaused
 16    StateGameOver
 17)
 18
 19type Game struct {
 20    State     GameState
 21    Score     int
 22    Level     int
 23    Lives     int
 24    IsRunning bool
 25}
 26
 27func main() {
 28    game := &Game{
 29        State:     StateMenu,
 30        Score:     0,
 31        Level:     1,
 32        Lives:     3,
 33        IsRunning: true,
 34    }
 35
 36    // Main game loop
 37    for game.IsRunning {
 38        switch game.State {
 39        case StateMenu:
 40            handleMenu(game)
 41        case StatePlaying:
 42            handlePlaying(game)
 43        case StatePaused:
 44            handlePaused(game)
 45        case StateGameOver:
 46            handleGameOver(game)
 47        }
 48
 49        time.Sleep(100 * time.Millisecond)
 50    }
 51
 52    fmt.Println("Game ended. Thanks for playing!")
 53}
 54
 55func handleMenu(game *Game) {
 56    fmt.Println("\n=== SPACE ADVENTURE ===")
 57    fmt.Println("1. Start Game")
 58    fmt.Println("2. Quit")
 59    fmt.Print("Choose option: ")
 60
 61    var choice int
 62    fmt.Scanln(&choice)
 63
 64    if choice == 1 {
 65        game.State = StatePlaying
 66        fmt.Println("Starting game...")
 67    } else {
 68        game.IsRunning = false
 69    }
 70}
 71
 72func handlePlaying(game *Game) {
 73    // Simulate game events
 74    event := rand.Intn(10)
 75
 76    switch {
 77    case event < 3: // Score points
 78        points := rand.Intn(100) + 10
 79        game.Score += points
 80        fmt.Printf("You scored %d points! Total: %d\n", points, game.Score)
 81
 82        // Check level progression
 83        if game.Score > game.Level*500 {
 84            game.Level++
 85            game.Lives++
 86            fmt.Printf("Level %d reached! Bonus life!\n", game.Level)
 87        }
 88
 89    case event < 6: // Take damage
 90        if game.Lives > 0 {
 91            game.Lives--
 92            fmt.Printf("Ouch! Lives remaining: %d\n", game.Lives)
 93
 94            if game.Lives == 0 {
 95                game.State = StateGameOver
 96            }
 97        }
 98
 99    case event < 8: // Find power-up
100        fmt.Println("Power-up found! Extra points!")
101        game.Score += 200
102
103    default: // Nothing happens
104        // Continue playing
105    }
106
107    // Random pause
108    if rand.Float64() < 0.05 {
109        fmt.Println("Game paused")
110        game.State = StatePaused
111    }
112}
113
114func handlePaused(game *Game) {
115    // Simple pause handling
116    fmt.Println("Game resumed")
117    game.State = StatePlaying
118}
119
120func handleGameOver(game *Game) {
121    fmt.Printf("\n=== GAME OVER ===\n")
122    fmt.Printf("Final Score: %d\n", game.Score)
123    fmt.Printf("Levels Reached: %d\n", game.Level)
124
125    fmt.Println("\n1. Play Again")
126    fmt.Println("2. Quit")
127    fmt.Print("Choose option: ")
128
129    var choice int
130    fmt.Scanln(&choice)
131
132    if choice == 1 {
133        // Reset game
134        game.Score = 0
135        game.Level = 1
136        game.Lives = 3
137        game.State = StateMenu
138    } else {
139        game.IsRunning = false
140    }
141}

Performance Considerations

Loop Optimization Tips

 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "time"
 7)
 8
 9func main() {
10    // Test different loop patterns
11    data := make([]int, 1000000)
12    for i := range data {
13        data[i] = i
14    }
15
16    // Pattern 1: Standard range
17    start := time.Now()
18    sum1 := 0
19    for _, v := range data {
20        sum1 += v
21    }
22    fmt.Printf("Range loop: %v, Sum: %d\n", time.Since(start), sum1)
23
24    // Pattern 2: Index access
25    start = time.Now()
26    sum2 := 0
27    for i := 0; i < len(data); i++ {
28        sum2 += data[i]
29    }
30    fmt.Printf("Index loop: %v, Sum: %d\n", time.Since(start), sum2)
31
32    // Pattern 3: Avoid repeated len() calls
33    start = time.Now()
34    sum3 := 0
35    n := len(data) // Cache length
36    for i := 0; i < n; i++ {
37        sum3 += data[i]
38    }
39    fmt.Printf("Cached length loop: %v, Sum: %d\n", time.Since(start), sum3)
40}

Early Exit Patterns

 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "math"
 7)
 8
 9func main() {
10    // Find first prime number greater than 1000
11    for i := 1001; ; i++ {
12        if isPrime(i) {
13            fmt.Printf("First prime > 1000: %d\n", i)
14            break // Exit early - no need to continue
15        }
16    }
17}
18
19func isPrime(n int) bool {
20    if n < 2 {
21        return false
22    }
23    if n == 2 {
24        return true
25    }
26    if n%2 == 0 {
27        return false
28    }
29
30    // Only check up to sqrt(n)
31    limit := int(math.Sqrt(float64(n)))
32    for i := 3; i <= limit; i += 2 {
33        if n%i == 0 {
34            return false
35        }
36    }
37
38    return true
39}

Practice Exercises

Exercise 1: Temperature Converter

🎯 Learning Objectives: Master switch statements and conditional logic for handling multiple conversion types with user input validation.

🌍 Real-World Context: Think of a weather application that needs to convert between different temperature units for international users - proper control flow ensures accurate conversions and meaningful error messages.

⭐ Difficulty: Beginner | ⏱️ Time Estimate: 15 minutes

Write a program that converts temperatures between Celsius and Fahrenheit based on user input.

Solution
 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    tempType := "C" // C for Celsius, F for Fahrenheit
 8    value := 25.0
 9
10    switch tempType {
11    case "C":
12        fahrenheit := value*9/5 + 32
13        fmt.Printf("%.1f°C = %.1f°F\n", value, fahrenheit)
14    case "F":
15        celsius := (value - 32) * 5 / 9
16        fmt.Printf("%.1f°F = %.1f°C\n", value, celsius)
17    default:
18        fmt.Println("Invalid temperature type")
19    }
20}

Exercise 2: Sum of Even Numbers

🎯 Learning Objectives: Practice for loop patterns with conditional statements and flow control using continue statements.

🌍 Real-World Context: Consider a billing system that processes only even-numbered invoice IDs for a specific batch - control flow statements help filter and process the right subset of data efficiently.

⭐ Difficulty: Beginner | ⏱️ Time Estimate: 15 minutes

Write a program that calculates the sum of all even numbers from 1 to 100.

Solution
 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    sum := 0
 8
 9    for i := 1; i <= 100; i++ {
10        if i%2 != 0 {
11            continue // Skip odd numbers
12        }
13        sum += i
14    }
15
16    fmt.Printf("Sum of even numbers from 1 to 100: %d\n", sum)
17}

Exercise 3: Prime Number Checker

🎯 Learning Objectives: Implement efficient algorithms using loops, conditional logic, and early termination with break statements.

🌍 Real-World Context: Think of a cryptographic system that needs to validate prime numbers for key generation - efficient prime checking with early exit improves performance significantly.

⭐ Difficulty: Intermediate | ⏱️ Time Estimate: 20 minutes

Write a function that checks if a number is prime.

Solution
 1// run
 2package main
 3
 4import "fmt"
 5
 6func isPrime(n int) bool {
 7    if n < 2 {
 8        return false
 9    }
10
11    for i := 2; i*i <= n; i++ {
12        if n%i == 0 {
13            return false
14        }
15    }
16
17    return true
18}
19
20func main() {
21    numbers := []int{2, 3, 4, 17, 20, 29, 100}
22
23    for _, num := range numbers {
24        if isPrime(num) {
25            fmt.Printf("%d is prime\n", num)
26        } else {
27            fmt.Printf("%d is not prime\n", num)
28        }
29    }
30}

Exercise 4: Word Counter

🎯 Learning Objectives: Master range loops over maps and string manipulation for data analysis and frequency counting.

🌍 Real-World Context: Consider a text analysis tool that counts word frequencies for SEO optimization or content analysis. Map iteration and range loops provide efficient data aggregation.

⭐ Difficulty: Intermediate | ⏱️ Time Estimate: 20 minutes

Count the frequency of each word in a sentence using range and map.

Solution
 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "strings"
 7)
 8
 9func main() {
10    sentence := "go is fun go is powerful go is simple"
11    words := strings.Fields(sentence)
12
13    frequency := make(map[string]int)
14
15    for _, word := range words {
16        frequency[word]++
17    }
18
19    for word, count := range frequency {
20        fmt.Printf("%s: %d\n", word, count)
21    }
22}

Exercise 5: Pattern Printer

🎯 Learning Objectives: Implement nested loops and pattern generation algorithms for complex visual output formatting.

🌍 Real-World Context: Think of a UI component library that needs to generate various visual patterns for progress indicators or loading animations. Nested loops provide the foundation for such visual programming.

⭐ Difficulty: Intermediate | ⏱️ Time Estimate: 25 minutes

Create a program that prints a pyramid pattern of asterisks.

Solution
 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    height := 5
 8
 9    for i := 1; i <= height; i++ {
10        // Print spaces
11        for j := 0; j < height-i; j++ {
12            fmt.Print(" ")
13        }
14
15        // Print asterisks
16        for k := 0; k < 2*i-1; k++ {
17            fmt.Print("*")
18        }
19
20        fmt.Println()
21    }
22}

Exercise 6: FizzBuzz

🎯 Learning Objectives: Practice conditional logic and multiple branching scenarios with modulo operations for classic algorithmic problems.

🌍 Real-World Context: Consider a game development system that needs to handle multiple game state conditions simultaneously. FizzBuzz teaches pattern recognition and multi-conditional logic essential for game mechanics.

⭐ Difficulty: Intermediate | ⏱️ Time Estimate: 20 minutes

Write a program that prints numbers from 1 to 100. For multiples of 3, print "Fizz"; for multiples of 5, print "Buzz"; for multiples of both, print "FizzBuzz".

Solution
 1// run
 2package main
 3
 4import "fmt"
 5
 6func fizzBuzz(n int) string {
 7    if n%15 == 0 {
 8        return "FizzBuzz"
 9    } else if n%3 == 0 {
10        return "Fizz"
11    } else if n%5 == 0 {
12        return "Buzz"
13    }
14    return fmt.Sprintf("%d", n)
15}
16
17func main() {
18    for i := 1; i <= 100; i++ {
19        fmt.Println(fizzBuzz(i))
20    }
21}

Exercise 7: Grade Calculator

🎯 Learning Objectives: Implement complex switch statements with expressions and range-based logic for grading systems.

🌍 Real-World Context: Think of an educational platform that automatically converts numeric scores to letter grades. Switch expressions provide clean, readable logic for multi-level categorization.

⭐ Difficulty: Intermediate | ⏱️ Time Estimate: 25 minutes

Create a function that takes a numerical score and returns a letter grade using switch statements.

Solution
 1// run
 2package main
 3
 4import "fmt"
 5
 6func getLetterGrade(score int) string {
 7    switch {
 8    case score >= 90:
 9        return "A"
10    case score >= 80:
11        return "B"
12    case score >= 70:
13        return "C"
14    case score >= 60:
15        return "D"
16    default:
17        return "F"
18    }
19}
20
21func main() {
22    scores := []int{95, 87, 82, 76, 73, 68, 55, 42}
23
24    for _, score := range scores {
25        grade := getLetterGrade(score)
26        fmt.Printf("Score %d: %s\n", score, grade)
27    }
28}

Exercise 8: Number Pyramid

🎯 Learning Objectives: Master complex nested loop patterns and string manipulation for creating structured numeric patterns.

🌍 Real-World Context: Consider a data visualization tool that creates hierarchical tree structures or pyramid charts. Nested loops with proper spacing control create the foundation for such visual algorithms.

⭐ Difficulty: Advanced | ⏱️ Time Estimate: 30 minutes

Write a program that prints a number pyramid using nested loops.

Solution
 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "strings"
 7)
 8
 9func printNumberPyramid(rows int) {
10    for i := 1; i <= rows; i++ {
11        // Print spaces
12        spaces := strings.Repeat(" ", rows-i)
13        fmt.Print(spaces)
14
15        // Print ascending numbers
16        for j := 1; j <= i; j++ {
17            fmt.Print(j)
18        }
19
20        // Print descending numbers
21        for j := i - 1; j >= 1; j-- {
22            fmt.Print(j)
23        }
24
25        fmt.Println()
26    }
27}
28
29func main() {
30    fmt.Println("Number Pyramid:")
31    printNumberPyramid(5)
32}

Exercise 9: Prime Number Finder

🎯 Learning Objectives: Implement efficient algorithms for prime number generation using Sieve of Eratosthenes and optimization techniques.

🌍 Real-World Context: Think of a cryptographic system that needs to generate prime numbers for RSA key pairs. Efficient prime finding algorithms are crucial for security system performance.

⭐ Difficulty: Advanced | ⏱️ Time Estimate: 35 minutes

Find all prime numbers up to a given number using loops and conditional logic.

Solution
 1// run
 2package main
 3
 4import "fmt"
 5
 6func isPrime(n int) bool {
 7    if n < 2 {
 8        return false
 9    }
10    if n == 2 {
11        return true
12    }
13    if n%2 == 0 {
14        return false
15    }
16
17    // Check odd divisors up to sqrt(n)
18    for i := 3; i*i <= n; i += 2 {
19        if n%i == 0 {
20            return false
21        }
22    }
23
24    return true
25}
26
27func findPrimes(limit int) []int {
28    primes := []int{}
29    for i := 2; i <= limit; i++ {
30        if isPrime(i) {
31            primes = append(primes, i)
32        }
33    }
34    return primes
35}
36
37func main() {
38    limit := 50
39
40    fmt.Printf("Prime numbers up to %d:\n", limit)
41    primes := findPrimes(limit)
42    fmt.Println(primes)
43
44    fmt.Printf("\nTotal primes found: %d\n", len(primes))
45}

Exercise 10: Menu-Driven Calculator

🎯 Learning Objectives: Build interactive applications combining loops, switch statements, and user input handling for menu-driven interfaces.

🌍 Real-World Context: Consider a command-line financial calculator that provides multiple calculation modes. Menu-driven interfaces with proper control flow are essential for user-friendly CLI applications.

⭐ Difficulty: Advanced | ⏱️ Time Estimate: 40 minutes

Create an interactive calculator with a menu system using switch and loops.

Solution
  1// run
  2package main
  3
  4import (
  5    "bufio"
  6    "fmt"
  7    "os"
  8    "strconv"
  9    "strings"
 10)
 11
 12func displayMenu() {
 13    fmt.Println("\n=== Calculator Menu ===")
 14    fmt.Println("1. Add")
 15    fmt.Println("2. Subtract")
 16    fmt.Println("3. Multiply")
 17    fmt.Println("4. Divide")
 18    fmt.Println("5. Power")
 19    fmt.Println("6. Square Root")
 20    fmt.Println("0. Exit")
 21    fmt.Print("Enter choice: ")
 22}
 23
 24func getFloat(prompt string) (float64, error) {
 25    reader := bufio.NewReader(os.Stdin)
 26    fmt.Print(prompt)
 27    input, _ := reader.ReadString('\n')
 28    input = strings.TrimSpace(input)
 29    return strconv.ParseFloat(input, 64)
 30}
 31
 32func power(base, exp float64) float64 {
 33    result := 1.0
 34    absExp := exp
 35    if exp < 0 {
 36        absExp = -exp
 37    }
 38
 39    for i := 0; i < int(absExp); i++ {
 40        result *= base
 41    }
 42
 43    if exp < 0 {
 44        return 1.0 / result
 45    }
 46    return result
 47}
 48
 49func sqrt(n float64) float64 {
 50    if n < 0 {
 51        return 0
 52    }
 53    if n == 0 {
 54        return 0
 55    }
 56
 57    // Newton's method
 58    guess := n / 2
 59    for i := 0; i < 10; i++ {
 60        guess = (guess + n/guess) / 2
 61    }
 62    return guess
 63}
 64
 65func main() {
 66    reader := bufio.NewReader(os.Stdin)
 67
 68    for {
 69        displayMenu()
 70
 71        input, _ := reader.ReadString('\n')
 72        choice := strings.TrimSpace(input)
 73
 74        if choice == "0" {
 75            fmt.Println("Goodbye!")
 76            break
 77        }
 78
 79        var num1, num2 float64
 80        var err error
 81
 82        switch choice {
 83        case "1", "2", "3", "4", "5":
 84            num1, err = getFloat("Enter first number: ")
 85            if err != nil {
 86                fmt.Println("Invalid input")
 87                continue
 88            }
 89
 90            num2, err = getFloat("Enter second number: ")
 91            if err != nil {
 92                fmt.Println("Invalid input")
 93                continue
 94            }
 95
 96        case "6":
 97            num1, err = getFloat("Enter number: ")
 98            if err != nil {
 99                fmt.Println("Invalid input")
100                continue
101            }
102
103        default:
104            fmt.Println("Invalid choice")
105            continue
106        }
107
108        switch choice {
109        case "1":
110            fmt.Printf("Result: %.2f + %.2f = %.2f\n", num1, num2, num1+num2)
111        case "2":
112            fmt.Printf("Result: %.2f - %.2f = %.2f\n", num1, num2, num1-num2)
113        case "3":
114            fmt.Printf("Result: %.2f × %.2f = %.2f\n", num1, num2, num1*num2)
115        case "4":
116            if num2 == 0 {
117                fmt.Println("Error: Division by zero")
118            } else {
119                fmt.Printf("Result: %.2f ÷ %.2f = %.2f\n", num1, num2, num1/num2)
120            }
121        case "5":
122            result := power(num1, num2)
123            fmt.Printf("Result: %.2f ^ %.2f = %.2f\n", num1, num2, result)
124        case "6":
125            if num1 < 0 {
126                fmt.Println("Error: Cannot calculate square root of negative number")
127            } else {
128                result := sqrt(num1)
129                fmt.Printf("Result: √%.2f = %.2f\n", num1, result)
130            }
131        }
132    }
133}

Exercise 11: Data Pipeline Processor

🎯 Learning Objectives: Implement data processing pipelines with conditional filtering, transformation, and aggregation using control flow structures.

🌍 Real-World Context: Think of an ETL system that processes streaming data. Control flow structures enable flexible data filtering and transformation pipelines.

⭐ Difficulty: Advanced | ⏱️ Time Estimate: 45 minutes

Create a data processing pipeline that filters, transforms, and aggregates data using various control flow patterns.

Solution
  1// run
  2package main
  3
  4import (
  5	"fmt"
  6	"math"
  7	"strings"
  8)
  9
 10type DataRecord struct {
 11	ID     int
 12	Name   string
 13	Value  float64
 14	Status string
 15	Category string
 16}
 17
 18type ProcessedData struct {
 19	Total       int
 20	ValidCount  int
 21	SumValue    float64
 22	AvgValue    float64
 23	MaxValue    float64
 24	MinValue    float64
 25	CategoryCounts map[string]int
 26	StatusCounts   map[string]int
 27	ValidRecords   []DataRecord
 28}
 29
 30func processData(records []DataRecord) ProcessedData {
 31	result := ProcessedData{
 32		CategoryCounts: make(map[string]int),
 33		StatusCounts:   make(map[string]int),
 34		ValidRecords:   make([]DataRecord, 0),
 35		MinValue:       math.MaxFloat64,
 36	}
 37
 38	for _, record := range records {
 39		result.Total++
 40
 41		// Skip invalid records
 42		if record.Name == "" || record.Value < 0 {
 43			continue
 44		}
 45
 46		// Process valid records
 47		result.ValidCount++
 48		result.SumValue += record.Value
 49		result.CategoryCounts[record.Category]++
 50		result.StatusCounts[record.Status]++
 51
 52		// Update min/max
 53		if record.Value > result.MaxValue {
 54			result.MaxValue = record.Value
 55		}
 56		if record.Value < result.MinValue {
 57			result.MinValue = record.Value
 58		}
 59
 60		// Apply transformations
 61		if strings.Contains(record.Name, "premium") {
 62			record.Value *= 1.2 // Premium boost
 63		}
 64
 65		result.ValidRecords = append(result.ValidRecords, record)
 66	}
 67
 68	// Calculate average
 69	if result.ValidCount > 0 {
 70		result.AvgValue = result.SumValue / float64(result.ValidCount)
 71	} else {
 72		result.MinValue = 0
 73	}
 74
 75	return result
 76}
 77
 78func main() {
 79	// Sample data
 80	records := []DataRecord{
 81		{1, "premium product", 100.0, "active", "electronics"},
 82		{2, "basic service", 25.0, "active", "software"},
 83		{3, "", 50.0, "inactive", "hardware"},        // Invalid
 84		{4, "premium plan", 75.0, "pending", "software"},
 85		{5, "standard item", 30.0, "active", "hardware"},
 86		{6, "enterprise solution", 200.0, "active", "software"},
 87		{7, "basic tool", -10.0, "active", "hardware"}, // Invalid
 88		{8, "premium widget", 85.0, "expired", "electronics"},
 89		{9, "starter pack", 15.0, "active", "software"},
 90		{10, "pro service", 120.0, "active", "electronics"},
 91	}
 92
 93	fmt.Println("=== Data Pipeline Processing Demo ===\n")
 94
 95	// Process all data
 96	fmt.Println("1. Overall Processing:")
 97	result := processData(records)
 98	fmt.Printf("   Total records: %d\n", result.Total)
 99	fmt.Printf("   Valid records: %d\n", result.ValidCount)
100	fmt.Printf("   Average value: %.2f\n", result.AvgValue)
101	fmt.Printf("   Value range: %.2f - %.2f\n", result.MinValue, result.MaxValue)
102	fmt.Printf("   Categories: %v\n", result.CategoryCounts)
103	fmt.Printf("   Statuses: %v\n", result.StatusCounts)
104}

Exercise 12: State Machine Implementation

🎯 Learning Objectives: Implement state machines with complex state transitions and event handling using switch statements and control flow.

🌍 Real-World Context: Consider a traffic light controller or order processing system. State machines provide reliable patterns for managing complex state-dependent behavior.

⭐ Difficulty: Advanced | ⏱️ Time Estimate: 50 minutes

Build a state machine for an order processing system with multiple states and transitions.

Solution
  1// run
  2package main
  3
  4import (
  5	"fmt"
  6	"strings"
  7	"time"
  8)
  9
 10type OrderState int
 11
 12const (
 13	StatePending OrderState = iota
 14	StateConfirmed
 15	StateProcessing
 16	StateShipped
 17	StateDelivered
 18	StateCancelled
 19	StateRefunded
 20)
 21
 22type OrderEvent int
 23
 24const (
 25	EventConfirm OrderEvent = iota
 26	EventStartProcessing
 27	EventShip
 28	EventDeliver
 29	EventCancel
 30	EventRefund
 31	EventReturn
 32)
 33
 34type Order struct {
 35	ID          int
 36	State       OrderState
 37	Total       float64
 38	Customer    string
 39	CreatedAt   time.Time
 40	UpdatedAt   time.Time
 41	History     []StateTransition
 42}
 43
 44type StateTransition struct {
 45	From     OrderState
 46	To       OrderState
 47	Event    OrderEvent
 48	Timestamp time.Time
 49	Note     string
 50}
 51
 52type StateMachine struct {
 53	allowedTransitions map[OrderState]map[OrderEvent]OrderState
 54}
 55
 56func NewStateMachine() *StateMachine {
 57	sm := &StateMachine{
 58		allowedTransitions: make(map[OrderState]map[OrderEvent]OrderState),
 59	}
 60
 61	// Define valid transitions
 62	sm.addTransition(StatePending, EventConfirm, StateConfirmed)
 63	sm.addTransition(StatePending, EventCancel, StateCancelled)
 64
 65	sm.addTransition(StateConfirmed, EventStartProcessing, StateProcessing)
 66	sm.addTransition(StateConfirmed, EventCancel, StateCancelled)
 67
 68	sm.addTransition(StateProcessing, EventShip, StateShipped)
 69	sm.addTransition(StateProcessing, EventCancel, StateCancelled)
 70
 71	sm.addTransition(StateShipped, EventDeliver, StateDelivered)
 72	sm.addTransition(StateShipped, EventReturn, StateProcessing)
 73
 74	sm.addTransition(StateDelivered, EventReturn, StateRefunded)
 75	sm.addTransition(StateDelivered, EventRefund, StateRefunded)
 76
 77	sm.addTransition(StateCancelled, EventRefund, StateRefunded)
 78
 79	return sm
 80}
 81
 82func (sm *StateMachine) addTransition(from OrderState, event OrderEvent, to OrderState) {
 83	if sm.allowedTransitions[from] == nil {
 84		sm.allowedTransitions[from] = make(map[OrderEvent]OrderState)
 85	}
 86	sm.allowedTransitions[from][event] = to
 87}
 88
 89func (sm *StateMachine) CanTransition(order *Order, event OrderEvent) bool {
 90	if transitions, exists := sm.allowedTransitions[order.State]; exists {
 91		if _, valid := transitions[event]; valid {
 92			return true
 93		}
 94	}
 95	return false
 96}
 97
 98func (sm *StateMachine) Transition(order *Order, event OrderEvent, note string) error {
 99	if !sm.CanTransition(order, event) {
100		return fmt.Errorf("invalid transition from %v with event %v", order.State, event)
101	}
102
103	newState := sm.allowedTransitions[order.State][event]
104
105	// Record transition
106	transition := StateTransition{
107		From:      order.State,
108		To:        newState,
109		Event:     event,
110		Timestamp: time.Now(),
111		Note:      note,
112	}
113
114	order.History = append(order.History, transition)
115	order.State = newState
116	order.UpdatedAt = time.Now()
117
118	return nil
119}
120
121func (o *Order) GetStateName() string {
122	switch o.State {
123	case StatePending:
124		return "Pending"
125	case StateConfirmed:
126		return "Confirmed"
127	case StateProcessing:
128		return "Processing"
129	case StateShipped:
130		return "Shipped"
131	case StateDelivered:
132		return "Delivered"
133	case StateCancelled:
134		return "Cancelled"
135	case StateRefunded:
136		return "Refunded"
137	default:
138		return "Unknown"
139	}
140}
141
142func (e OrderEvent) GetEventName() string {
143	switch e {
144	case EventConfirm:
145		return "Confirm"
146	case EventStartProcessing:
147		return "Start Processing"
148	case EventShip:
149		return "Ship"
150	case EventDeliver:
151		return "Deliver"
152	case EventCancel:
153		return "Cancel"
154	case EventRefund:
155		return "Refund"
156	case EventReturn:
157		return "Return"
158	default:
159		return "Unknown"
160	}
161}
162
163func processOrderWorkflow(sm *StateMachine, order *Order) {
164	fmt.Printf("\n=== Processing Order %d ===\n", order.ID)
165	fmt.Printf("Customer: %s\n", order.Customer)
166	fmt.Printf("Total: $%.2f\n", order.Total)
167
168	// Simulate order processing workflow
169	events := []struct {
170		event OrderEvent
171		note  string
172		delay time.Duration
173	}{
174		{EventConfirm, "Order confirmed by customer", 500 * time.Millisecond},
175		{EventStartProcessing, "Started processing in warehouse", 1000 * time.Millisecond},
176		{EventShip, "Order shipped with tracking", 800 * time.Millisecond},
177		{EventDeliver, "Order delivered successfully", 600 * time.Millisecond},
178	}
179
180	for _, step := range events {
181		time.Sleep(step.delay)
182
183		if err := sm.Transition(order, step.event, step.note); err != nil {
184			fmt.Printf("❌ Error: %v\n", err)
185			return
186		}
187
188		fmt.Printf("✓ %s → %s (%s)\n",
189			step.event.GetEventName(),
190			order.GetStateName(),
191			step.note)
192	}
193
194	// Show final state
195	fmt.Printf("\nFinal State: %s\n", order.GetStateName())
196}
197
198func main() {
199	sm := NewStateMachine()
200
201	// Process normal order
202	order1 := &Order{
203		ID:        1001,
204		State:     StatePending,
205		Total:     129.99,
206		Customer:  "John Doe",
207		CreatedAt: time.Now(),
208		UpdatedAt: time.Now(),
209		History:   []StateTransition{},
210	}
211
212	processOrderWorkflow(sm, order1)
213
214	// Process order with cancellation
215	fmt.Println("\n" + strings.Repeat("=", 50))
216	order2 := &Order{
217		ID:        1002,
218		State:     StatePending,
219		Total:     89.50,
220		Customer:  "Jane Smith",
221		CreatedAt: time.Now(),
222		UpdatedAt: time.Now(),
223		History:   []StateTransition{},
224	}
225
226	fmt.Printf("\n=== Processing Order %d ===\n", order2.ID)
227
228	// Confirm first
229	sm.Transition(order2, EventConfirm, "Order confirmed")
230	fmt.Printf("✓ %s → %s\n", EventConfirm.GetEventName(), order2.GetStateName())
231
232	// Then cancel
233	time.Sleep(500 * time.Millisecond)
234	sm.Transition(order2, EventCancel, "Customer requested cancellation")
235	fmt.Printf("✓ %s → %s\n", EventCancel.GetEventName(), order2.GetStateName())
236
237	// Try to refund
238	time.Sleep(300 * time.Millisecond)
239	sm.Transition(order2, EventRefund, "Processing refund")
240	fmt.Printf("✓ %s → %s\n", EventRefund.GetEventName(), order2.GetStateName())
241
242	fmt.Println("\n=== State Machine Demo Complete ===")
243}

Summary

Key Takeaways

  1. Go's Philosophy: "Less is more" - fewer constructs but more powerful
  2. If Statements: Use initialization for clean, scoped variables
  3. For Loops: One loop handles all cases
  4. Switch Statements: Clean multi-way branching without fallthrough by default
  5. Defer: Essential for reliable resource cleanup
  6. Range: Safe, efficient way to iterate over collections
  7. Performance: Go's control structures are highly optimized
  8. Pitfalls: Watch for slice modification, off-by-one, and scope issues

Best Practices

  • Use range for collection iteration - safer and more readable
  • Initialize variables in if when possible - limited scope
  • Always have exit conditions in infinite loops
  • Use switch for multi-way branching - cleaner than long if-else chains
  • defer resource cleanup - ensures reliability
  • Cache len() calls in tight loops when it doesn't change
  • Break early when possible - improves performance

Next Steps

  • Practice: Implement the exercises below to reinforce concepts
  • Explore: Learn about goroutines and channels for concurrent control flow
  • Apply: Use these patterns in your own projects
  • Advanced: Study algorithms that heavily use control flow

Control flow is the foundation of all programming. Master these Go patterns and you'll write clean, efficient, and reliable code!