1. go
  2. /basics
  3. /control-flow

Understanding Control Flow Structures in Go

Control flow structures in Go allow you to control the execution path of your program. This guide covers all the control flow statements available in Go and their best practices.

If Statements

Basic If Statement

if condition {
    // Code executed if condition is true
}

// Example
if age >= 18 {
    fmt.Println("Adult")
}

If-Else Statement

if condition {
    // Code executed if condition is true
} else {
    // Code executed if condition is false
}

// Example
if score >= 60 {
    fmt.Println("Pass")
} else {
    fmt.Println("Fail")
}

If-Else If-Else Chain

if condition1 {
    // Code for condition1
} else if condition2 {
    // Code for condition2
} else {
    // Code if no condition is true
}

// Example
if score >= 90 {
    fmt.Println("A")
} else if score >= 80 {
    fmt.Println("B")
} else if score >= 70 {
    fmt.Println("C")
} else {
    fmt.Println("F")
}

If with Initialization

if initialization; condition {
    // Code executed if condition is true
}

// Example
if value, err := someFunction(); err == nil {
    fmt.Printf("Value: %v\n", value)
} else {
    fmt.Printf("Error: %v\n", err)
}

For Loops

Go has only one looping construct: the for loop. However, it's very flexible and can be used in several ways.

Basic For Loop

for initialization; condition; post {
    // Loop body
}

// Example
for i := 0; i < 5; i++ {
    fmt.Printf("Iteration %d\n", i)
}

While-Style For Loop

for condition {
    // Loop body
}

// Example
count := 0
for count < 5 {
    fmt.Printf("Count: %d\n", count)
    count++
}

Infinite Loop

for {
    // Loop body
    if shouldBreak {
        break
    }
}

// Example
for {
    fmt.Println("Press 'q' to quit")
    if input == "q" {
        break
    }
}

Range-Based For Loop

// Iterating over slices
numbers := []int{1, 2, 3, 4, 5}
for index, value := range numbers {
    fmt.Printf("Index: %d, Value: %d\n", index, value)
}

// Iterating over maps
colors := map[string]string{"red": "#ff0000", "green": "#00ff00"}
for key, value := range colors {
    fmt.Printf("Key: %s, Value: %s\n", key, value)
}

// Iterating over strings
for index, char := range "Hello" {
    fmt.Printf("Index: %d, Char: %c\n", index, char)
}

// Iterating over channels
for item := range channel {
    fmt.Printf("Received: %v\n", item)
}

Switch Statements

Basic Switch

switch value {
case value1:
    // Code for value1
case value2:
    // Code for value2
default:
    // Default code
}

// Example
switch day {
case "Monday":
    fmt.Println("Start of work week")
case "Friday":
    fmt.Println("TGIF!")
default:
    fmt.Println("Regular day")
}

Switch with Initialization

switch initialization; value {
case value1:
    // Code for value1
case value2:
    // Code for value2
}

// Example
switch os := runtime.GOOS; os {
case "darwin":
    fmt.Println("macOS")
case "linux":
    fmt.Println("Linux")
default:
    fmt.Printf("%s\n", os)
}

Switch without Expression

switch {
case condition1:
    // Code for condition1
case condition2:
    // Code for condition2
default:
    // Default code
}

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

Switch with Multiple Cases

switch value {
case value1, value2:
    // Code for value1 or value2
case value3, value4:
    // Code for value3 or value4
}

// Example
switch char {
case 'a', 'e', 'i', 'o', 'u':
    fmt.Println("Vowel")
default:
    fmt.Println("Consonant")
}

Control Flow Keywords

Break Statement

// Break from loop
for i := 0; i < 10; i++ {
    if i == 5 {
        break
    }
    fmt.Println(i)
}

// Break from switch
switch value {
case 1:
    if condition {
        break
    }
    fmt.Println("More code")
}

Continue Statement

// Skip rest of current iteration
for i := 0; i < 5; i++ {
    if i == 2 {
        continue
    }
    fmt.Println(i)
}

Goto Statement

// Use goto sparingly!
func example() {
    if errorCondition {
        goto cleanup
    }
    // Normal processing
cleanup:
    // Cleanup code
}

Label Statements

// Label with break
OuterLoop:
    for i := 0; i < 5; i++ {
        for j := 0; j < 5; j++ {
            if i*j > 10 {
                break OuterLoop
            }
        }
    }

// Label with continue
OuterLoop:
    for i := 0; i < 5; i++ {
        for j := 0; j < 5; j++ {
            if j == 2 {
                continue OuterLoop
            }
        }
    }

Best Practices

1. Keep It Simple

// Good: Simple and clear
if user.IsAdmin {
    handleAdmin()
} else {
    handleUser()
}

// Avoid: Complex nested conditions
if user.IsActive {
    if user.IsAdmin {
        if user.HasPermission("delete") {
            // Too many levels
        }
    }
}

2. Early Returns

// Good: Early returns
func process(data []int) error {
    if len(data) == 0 {
        return errors.New("empty data")
    }
    
    if !isValid(data) {
        return errors.New("invalid data")
    }
    
    // Process valid data
    return nil
}

3. Switch vs If-Else

// Good: Use switch for multiple conditions
switch status {
case "active":
    handleActive()
case "inactive":
    handleInactive()
case "pending":
    handlePending()
default:
    handleUnknown()
}

// Avoid: Long if-else chains
if status == "active" {
    handleActive()
} else if status == "inactive" {
    handleInactive()
} else if status == "pending" {
    handlePending()
} else {
    handleUnknown()
}

Common Patterns

1. Error Handling

if err := doSomething(); err != nil {
    return fmt.Errorf("failed to do something: %w", err)
}

2. Resource Cleanup

file, err := os.Open("file.txt")
if err != nil {
    return err
}
defer file.Close()

// Process file...

3. Type Assertion

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

Next Steps

  1. Learn about functions
  2. Explore error handling
  3. Study packages
  4. Practice with data types

Additional Resources