Skip to main content

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 termination
  • continue 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!