Go Loop Structures
Loop structures are fundamental programming constructs that allow you to repeat operations and process collections of data efficiently. Go provides a clean and powerful set of loop constructs that enable you to write expressive and maintainable iterative code. This comprehensive guide will teach you everything you need to know about Go's loop structures, from basic for
loops to advanced range
loops and sophisticated iteration patterns.
Understanding Loops in Go
What Are Loops?
Loops are programming constructs that allow you to execute a block of code repeatedly until a certain condition is met. They are essential for:
- Processing collections of data (arrays, slices, maps)
- Repeating operations until a condition is satisfied
- Implementing algorithms that require iteration
- Handling user input and validation
- Performing calculations on large datasets
Go's Approach to Loops
Go's loop structures are designed with simplicity and clarity in mind:
Single Loop Type
Go provides only one loop construct: the for
loop. However, this single construct is incredibly flexible and can handle all looping scenarios.
Clean Syntax
Go's loop syntax is clean and readable, eliminating unnecessary complexity while maintaining power.
Range-Based Iteration
Go's range
keyword provides elegant iteration over collections without manual index management.
No Traditional While Loops
Go doesn't have traditional while
or do-while
loops, but the for
loop can handle all these scenarios.
Basic for Loops
Traditional for Loop
The traditional for
loop in Go follows the classic C-style syntax with initialization, condition, and increment.
package main
import "fmt"
func main() {
// Traditional for loop
fmt.Println("Traditional for loop:")
for i := 0; i < 5; i++ {
fmt.Printf("Iteration %d\n", i)
}
// Output:
// Iteration 0
// Iteration 1
// Iteration 2
// Iteration 3
// Iteration 4
// For loop with different increment
fmt.Println("\nFor loop with different increment:")
for i := 0; i < 10; i += 2 {
fmt.Printf("Even number: %d\n", i)
}
// Output:
// Even number: 0
// Even number: 2
// Even number: 4
// Even number: 6
// Even number: 8
// For loop with decrement
fmt.Println("\nFor loop with decrement:")
for i := 5; i > 0; i-- {
fmt.Printf("Countdown: %d\n", i)
}
// Output:
// Countdown: 5
// Countdown: 4
// Countdown: 3
// Countdown: 2
// Countdown: 1
// For loop with multiple variables
fmt.Println("\nFor loop with multiple variables:")
for i, j := 0, 10; i < 5; i, j = i+1, j-1 {
fmt.Printf("i=%d, j=%d\n", i, j)
}
// Output:
// i=0, j=10
// i=1, j=9
// i=2, j=8
// i=3, j=7
// i=4, j=6
}
While-Style for Loop
Go allows you to create while-style loops by omitting the initialization and increment parts of the for
loop.
package main
import "fmt"
func main() {
// While-style for loop
fmt.Println("While-style for loop:")
counter := 0
for counter < 5 {
fmt.Printf("Counter: %d\n", counter)
counter++
}
// Output:
// Counter: 0
// Counter: 1
// Counter: 2
// Counter: 3
// Counter: 4
// While-style loop with user input simulation
fmt.Println("\nWhile-style loop with condition:")
number := 1
for number < 100 {
fmt.Printf("Number: %d\n", number)
number *= 2
}
// Output:
// Number: 1
// Number: 2
// Number: 4
// Number: 8
// Number: 16
// Number: 32
// Number: 64
// Infinite loop with break (simulated)
fmt.Println("\nInfinite loop with break:")
count := 0
for {
if count >= 3 {
break
}
fmt.Printf("Infinite loop iteration: %d\n", count)
count++
}
// Output:
// Infinite loop iteration: 0
// Infinite loop iteration: 1
// Infinite loop iteration: 2
}
Infinite for Loop
Go allows you to create infinite loops by omitting all parts of the for
loop condition.
package main
import (
"fmt"
"time"
)
func main() {
// Infinite loop with break condition
fmt.Println("Infinite loop with break:")
counter := 0
for {
fmt.Printf("Infinite loop iteration: %d\n", counter)
counter++
if counter >= 5 {
break
}
}
// Output:
// Infinite loop iteration: 0
// Infinite loop iteration: 1
// Infinite loop iteration: 2
// Infinite loop iteration: 3
// Infinite loop iteration: 4
// Infinite loop with continue
fmt.Println("\nInfinite loop with continue:")
number := 0
for {
number++
if number%2 == 0 {
continue // Skip even numbers
}
fmt.Printf("Odd number: %d\n", number)
if number >= 10 {
break
}
}
// Output:
// Odd number: 1
// Odd number: 3
// Odd number: 5
// Odd number: 7
// Odd number: 9
}
Range Loops
Range with Slices
The range
keyword provides elegant iteration over slices, returning both index and value.
package main
import "fmt"
func main() {
// Range with slices
fmt.Println("Range with slices:")
fruits := []string{"apple", "banana", "orange", "grape"}
// Range with index and value
for index, fruit := range fruits {
fmt.Printf("Index %d: %s\n", index, fruit)
}
// Output:
// Index 0: apple
// Index 1: banana
// Index 2: orange
// Index 3: grape
// Range with value only
fmt.Println("\nRange with value only:")
for _, fruit := range fruits {
fmt.Printf("Fruit: %s\n", fruit)
}
// Output:
// Fruit: apple
// Fruit: banana
// Fruit: orange
// Fruit: grape
// Range with index only
fmt.Println("\nRange with index only:")
for index := range fruits {
fmt.Printf("Index: %d\n", index)
}
// Output:
// Index: 0
// Index: 1
// Index: 2
// Index: 3
// Range with integer slice
fmt.Println("\nRange with integer slice:")
numbers := []int{10, 20, 30, 40, 50}
sum := 0
for _, num := range numbers {
sum += num
fmt.Printf("Adding %d, sum: %d\n", num, sum)
}
// Output:
// Adding 10, sum: 10
// Adding 20, sum: 30
// Adding 30, sum: 60
// Adding 40, sum: 100
// Adding 50, sum: 150
}
Range with Maps
The range
keyword works seamlessly with maps, providing key-value pairs for iteration.
package main
import "fmt"
func main() {
// Range with maps
fmt.Println("Range with maps:")
person := map[string]interface{}{
"name": "John Doe",
"age": 30,
"city": "New York",
"country": "USA",
}
// Range with key and value
for key, value := range person {
fmt.Printf("%s: %v\n", key, value)
}
// Output (order may vary):
// name: John Doe
// age: 30
// city: New York
// country: USA
// Range with key only
fmt.Println("\nRange with key only:")
for key := range person {
fmt.Printf("Key: %s\n", key)
}
// Output (order may vary):
// Key: name
// Key: age
// Key: city
// Key: country
// Range with string map
fmt.Println("\nRange with string map:")
colors := map[string]string{
"red": "#FF0000",
"green": "#00FF00",
"blue": "#0000FF",
"yellow": "#FFFF00",
}
for color, hex := range colors {
fmt.Printf("%s: %s\n", color, hex)
}
// Output (order may vary):
// red: #FF0000
// green: #00FF00
// blue: #0000FF
// yellow: #FFFF00
}
Range with Strings
The range
keyword can iterate over strings, providing both byte index and Unicode character.
package main
import "fmt"
func main() {
// Range with strings
fmt.Println("Range with strings:")
text := "Hello, 世界!"
// Range with index and character
for index, char := range text {
fmt.Printf("Index %d: %c (Unicode: %U)\n", index, char, char)
}
// Output:
// Index 0: H (Unicode: U+0048)
// Index 1: e (Unicode: U+004C)
// Index 2: l (Unicode: U+006C)
// Index 3: l (Unicode: U+006C)
// Index 4: o (Unicode: U+006F)
// Index 5: , (Unicode: U+002C)
// Index 6: (Unicode: U+0020)
// Index 7: 世 (Unicode: U+4E16)
// Index 10: 界 (Unicode: U+754C)
// Index 13: ! (Unicode: U+0021)
// Range with character only
fmt.Println("\nRange with character only:")
for _, char := range text {
if char >= 'a' && char <= 'z' {
fmt.Printf("Lowercase letter: %c\n", char)
} else if char >= 'A' && char <= 'Z' {
fmt.Printf("Uppercase letter: %c\n", char)
}
}
// Output:
// Uppercase letter: H
// Lowercase letter: e
// Lowercase letter: l
// Lowercase letter: l
// Lowercase letter: o
// Range with index only
fmt.Println("\nRange with index only:")
for index := range text {
fmt.Printf("Byte index: %d\n", index)
}
// Output:
// Byte index: 0
// Byte index: 1
// Byte index: 2
// Byte index: 3
// Byte index: 4
// Byte index: 5
// Byte index: 6
// Byte index: 7
// Byte index: 10
// Byte index: 13
}
Loop Control Statements
Break Statement
The break
statement allows you to exit a loop immediately, regardless of the loop condition.
package main
import "fmt"
func main() {
// Break statement
fmt.Println("Break statement:")
for i := 0; i < 10; i++ {
if i == 5 {
fmt.Println("Breaking at 5")
break
}
fmt.Printf("Iteration: %d\n", i)
}
// Output:
// Iteration: 0
// Iteration: 1
// Iteration: 2
// Iteration: 3
// Iteration: 4
// Breaking at 5
// Break in nested loops
fmt.Println("\nBreak in nested loops:")
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
fmt.Println("Breaking inner loop")
break
}
fmt.Printf("i=%d, j=%d\n", i, j)
}
}
// Output:
// i=0, j=0
// i=0, j=1
// i=0, j=2
// i=1, j=0
// Breaking inner loop
// i=2, j=0
// i=2, j=1
// i=2, j=2
// Break with range
fmt.Println("\nBreak with range:")
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
for _, num := range numbers {
if num > 5 {
fmt.Println("Breaking when number > 5")
break
}
fmt.Printf("Number: %d\n", num)
}
// Output:
// Number: 1
// Number: 2
// Number: 3
// Number: 4
// Number: 5
// Breaking when number > 5
}
Continue Statement
The continue
statement allows you to skip the rest of the current iteration and proceed to the next iteration.
package main
import "fmt"
func main() {
// Continue statement
fmt.Println("Continue statement:")
for i := 0; i < 10; i++ {
if i%2 == 0 {
continue // Skip even numbers
}
fmt.Printf("Odd number: %d\n", i)
}
// Output:
// Odd number: 1
// Odd number: 3
// Odd number: 5
// Odd number: 7
// Odd number: 9
// Continue with range
fmt.Println("\nContinue with range:")
fruits := []string{"apple", "banana", "orange", "grape", "kiwi"}
for _, fruit := range fruits {
if len(fruit) < 5 {
continue // Skip short fruit names
}
fmt.Printf("Long fruit name: %s\n", fruit)
}
// Output:
// Long fruit name: apple
// Long fruit name: banana
// Long fruit name: orange
// Long fruit name: grape
// Continue in nested loops
fmt.Println("\nContinue in nested loops:")
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == j {
continue // Skip when i equals j
}
fmt.Printf("i=%d, j=%d\n", i, j)
}
}
// Output:
// i=0, j=1
// i=0, j=2
// i=1, j=0
// i=1, j=2
// i=2, j=0
// i=2, j=1
}
Nested Loops
Basic Nested Loops
Nested loops allow you to create complex iteration patterns and work with multidimensional data structures.
package main
import "fmt"
func main() {
// Basic nested loops
fmt.Println("Basic nested loops:")
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
fmt.Printf("(%d, %d) ", i, j)
}
fmt.Println()
}
// Output:
// (0, 0) (0, 1) (0, 2)
// (1, 0) (1, 1) (1, 2)
// (2, 0) (2, 1) (2, 2)
// Nested loops with different ranges
fmt.Println("\nNested loops with different ranges:")
for i := 1; i <= 3; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d ", j)
}
fmt.Println()
}
// Output:
// 1
// 1 2
// 1 2 3
// Nested loops with multiplication table
fmt.Println("\nMultiplication table:")
for i := 1; i <= 5; i++ {
for j := 1; j <= 5; j++ {
fmt.Printf("%2d ", i*j)
}
fmt.Println()
}
// Output:
// 1 2 3 4 5
// 2 4 6 8 10
// 3 6 9 12 15
// 4 8 12 16 20
// 5 10 15 20 25
}
Nested Loops with Break and Continue
Nested loops can use break
and continue
statements to control flow at different levels.
package main
import "fmt"
func main() {
// Nested loops with break
fmt.Println("Nested loops with break:")
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
fmt.Println("Breaking inner loop")
break
}
fmt.Printf("(%d, %d) ", i, j)
}
fmt.Println()
}
// Output:
// (0, 0) (0, 1) (0, 2)
// (1, 0)
// Breaking inner loop
// (2, 0) (2, 1) (2, 2)
// Nested loops with continue
fmt.Println("\nNested loops with continue:")
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == j {
continue // Skip when i equals j
}
fmt.Printf("(%d, %d) ", i, j)
}
fmt.Println()
}
// Output:
// (0, 1) (0, 2)
// (1, 0) (1, 2)
// (2, 0) (2, 1)
// Nested loops with labeled break
fmt.Println("\nNested loops with labeled break:")
outerLoop:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
fmt.Println("Breaking outer loop")
break outerLoop
}
fmt.Printf("(%d, %d) ", i, j)
}
fmt.Println()
}
// Output:
// (0, 0) (0, 1) (0, 2)
// (1, 0)
// Breaking outer loop
}
Advanced Loop Patterns
Loop with Function Calls
Loops can incorporate function calls to create dynamic and flexible iteration patterns.
package main
import (
"fmt"
"math/rand"
"time"
)
// Helper function to generate random numbers
func generateRandomNumber() int {
return rand.Intn(100)
}
// Helper function to check if number is prime
func isPrime(n int) bool {
if n < 2 {
return false
}
for i := 2; i*i <= n; i++ {
if n%i == 0 {
return false
}
}
return true
}
func main() {
rand.Seed(time.Now().UnixNano())
// Loop with function calls
fmt.Println("Loop with function calls:")
for i := 0; i < 5; i++ {
num := generateRandomNumber()
fmt.Printf("Random number: %d\n", num)
}
// Output (example):
// Random number: 42
// Random number: 17
// Random number: 89
// Random number: 3
// Random number: 67
// Loop with conditional function calls
fmt.Println("\nLoop with conditional function calls:")
for i := 0; i < 10; i++ {
num := generateRandomNumber()
if isPrime(num) {
fmt.Printf("Prime number found: %d\n", num)
} else {
fmt.Printf("Non-prime number: %d\n", num)
}
}
// Output (example):
// Non-prime number: 42
// Prime number found: 17
// Prime number found: 89
// Prime number found: 3
// Non-prime number: 67
// ...
}
Loop with Error Handling
Loops can incorporate error handling to create robust iteration patterns.
package main
import (
"fmt"
"strconv"
)
func main() {
// Loop with error handling
fmt.Println("Loop with error handling:")
inputs := []string{"123", "abc", "456", "def", "789"}
for i, input := range inputs {
if num, err := strconv.Atoi(input); err == nil {
fmt.Printf("Input %d: '%s' -> %d\n", i+1, input, num)
} else {
fmt.Printf("Input %d: '%s' -> Error: %v\n", i+1, input, err)
}
}
// Output:
// Input 1: '123' -> 123
// Input 2: 'abc' -> Error: strconv.Atoi: parsing "abc": invalid syntax
// Input 3: '456' -> 456
// Input 4: 'def' -> Error: strconv.Atoi: parsing "def": invalid syntax
// Input 5: '789' -> 789
// Loop with validation
fmt.Println("\nLoop with validation:")
numbers := []int{-5, 0, 5, 10, 15, 20, 25, 30}
for _, num := range numbers {
if num < 0 {
fmt.Printf("Negative number: %d (skipping)\n", num)
continue
}
if num > 20 {
fmt.Printf("Large number: %d (stopping)\n", num)
break
}
fmt.Printf("Valid number: %d\n", num)
}
// Output:
// Negative number: -5 (skipping)
// Valid number: 0
// Valid number: 5
// Valid number: 10
// Valid number: 15
// Valid number: 20
// Large number: 25 (stopping)
}
Performance Considerations
Loop Performance Optimization
Understanding loop performance characteristics is crucial for writing efficient Go programs.
package main
import (
"fmt"
"time"
)
func main() {
// Performance comparison: different loop styles
fmt.Println("Performance comparison:")
// Test data
size := 1000000
slice := make([]int, size)
for i := range slice {
slice[i] = i
}
// Traditional for loop
start := time.Now()
sum1 := 0
for i := 0; i < len(slice); i++ {
sum1 += slice[i]
}
duration1 := time.Since(start)
fmt.Printf("Traditional for loop: %v, sum: %d\n", duration1, sum1)
// Range loop
start = time.Now()
sum2 := 0
for _, value := range slice {
sum2 += value
}
duration2 := time.Since(start)
fmt.Printf("Range loop: %v, sum: %d\n", duration2, sum2)
// Range loop with index
start = time.Now()
sum3 := 0
for i := range slice {
sum3 += slice[i]
}
duration3 := time.Since(start)
fmt.Printf("Range loop with index: %v, sum: %d\n", duration3, sum3)
// Output (example):
// Traditional for loop: 2.5ms, sum: 499999500000
// Range loop: 2.3ms, sum: 499999500000
// Range loop with index: 2.4ms, sum: 499999500000
}
What You've Learned
Congratulations! You now have a comprehensive understanding of Go's loop structures:
Basic for Loops
- Traditional C-style for loops with initialization, condition, and increment
- While-style for loops for conditional iteration
- Infinite for loops with break conditions
- Multiple variable initialization and increment
Range Loops
- Range iteration over slices with index and value
- Range iteration over maps with key and value pairs
- Range iteration over strings with Unicode character handling
- Selective iteration with index-only or value-only patterns
Loop Control
break
statement for immediate loop terminationcontinue
statement for skipping current iteration- Nested loop control with labeled breaks
- Conditional loop control patterns
Advanced Patterns
- Nested loops for multidimensional data processing
- Function calls within loops for dynamic behavior
- Error handling in loop iterations
- Performance optimization techniques
Best Practices
- Choosing appropriate loop types for different scenarios
- Writing efficient and readable loop code
- Handling edge cases and error conditions
- Optimizing loop performance
Next Steps
You now have a solid foundation in Go's loop structures. In the next section, we'll explore advanced control flow patterns, including labeled statements, goto statements, and complex control flow scenarios that will enable you to handle sophisticated programming logic.
Understanding loop structures is crucial for processing data collections and implementing iterative algorithms. These concepts form the foundation for all the more advanced programming techniques we'll cover in the coming chapters.
Ready to learn about advanced control flow? Let's explore Go's advanced control flow patterns and learn how to handle complex programming scenarios!