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
- Learn about functions
- Explore error handling
- Study packages
- Practice with data types