Skip to main content

Go For Loops

The for loop is Go's only looping construct, but don't let that fool you - it's incredibly versatile! Unlike many other programming languages that have separate loop constructs like while and do-while, Go simplifies things by providing a single, flexible looping structure that can be adapted to handle all your iteration needs.

Introduction to For Loops

In Go, the for loop serves as the universal tool for repetitive tasks. Whether you need to iterate a specific number of times, loop while a condition is true, or process items in a collection, the for loop has you covered.

tip

If you're coming from other programming languages, you'll find Go's approach refreshingly simple - just one loop construct to learn!

Basic For Loop Syntax

Let's start with the most common form of the for loop, which looks similar to what you might find in C, Java, or JavaScript:

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

Where:

  • initialization: Executes once before the loop starts
  • condition: Checked before each iteration; the loop continues while this is true
  • post: Executes at the end of each iteration

Here's a simple example:

go
package main

import "fmt"

func main() {
for i := 0; i < 5; i++ {
fmt.Println("Iteration:", i)
}
}

Output:

Iteration: 0
Iteration: 1
Iteration: 2
Iteration: 3
Iteration: 4

How It Works:

  1. i := 0 - We initialize a variable i to 0 (this runs once)
  2. i < 5 - Before each iteration, we check if i is less than 5
  3. If the condition is true, we execute the loop body
  4. i++ - After each iteration, we increment i
  5. Repeat steps 2-4 until the condition becomes false

For as a While Loop

Go doesn't have a dedicated while loop. Instead, you can use the for loop with just a condition:

go
package main

import "fmt"

func main() {
sum := 1
for sum < 100 {
sum += sum
fmt.Println("Current sum:", sum)
}
fmt.Println("Final sum:", sum)
}

Output:

Current sum: 2
Current sum: 4
Current sum: 8
Current sum: 16
Current sum: 32
Current sum: 64
Current sum: 128
Final sum: 128

In this example, we're doubling sum repeatedly until it exceeds 100. The loop behaves like a while loop in other languages.

Infinite Loops

You can create an infinite loop by omitting the condition entirely:

go
for {
// This will run forever unless broken
if someCondition {
break // Exit the loop
}
}

Here's a practical example:

go
package main

import (
"fmt"
"math/rand"
"time"
)

func main() {
// Seed the random number generator
rand.Seed(time.Now().UnixNano())

counter := 0

for {
// Generate random number between 0 and 9
num := rand.Intn(10)
counter++

fmt.Printf("Attempt %d: Got number %d
", counter, num)

// Exit when we get a 7
if num == 7 {
fmt.Printf("Found 7 after %d attempts!
", counter)
break
}

// Safety valve - don't run more than 20 iterations
if counter >= 20 {
fmt.Println("Giving up after 20 attempts")
break
}
}
}

This will keep generating random numbers until it either finds a 7 or makes 20 attempts.

caution

Always ensure infinite loops have a way to terminate, such as a break statement, or they'll run forever and potentially crash your program!

For-Range Loop

One of the most powerful variations of the for loop in Go is the "for-range" loop, which is used to iterate over elements in various data structures:

go
package main

import "fmt"

func main() {
// Iterating over a slice
fruits := []string{"apple", "banana", "cherry", "dragonfruit"}

fmt.Println("Fruits in my basket:")
for index, fruit := range fruits {
fmt.Printf("%d: %s
", index, fruit)
}

// Iterating over a map
calories := map[string]int{
"apple": 52,
"banana": 96,
"cherry": 50,
"dragonfruit": 60,
}

fmt.Println("
Calorie content:")
for fruit, cal := range calories {
fmt.Printf("%s: %d calories
", fruit, cal)
}
}

Output:

Fruits in my basket:
0: apple
1: banana
2: cherry
3: dragonfruit

Calorie content:
apple: 52 calories
banana: 96 calories
cherry: 50 calories
dragonfruit: 60 calories

The range form of the for loop iterates over:

  • Arrays or slices
  • Strings (by rune)
  • Maps
  • Channels

What You Get With Range

The values returned by range depend on the type being iterated:

TypeFirst ValueSecond Value
Array/SliceIndexValue at index
StringIndexUnicode code point (rune)
MapKeyValue
ChannelValue-

Using the Blank Identifier

If you only need one of the values from range, you can use the blank identifier (_) to ignore the other:

go
// If you only need the values, not the indices
for _, fruit := range fruits {
fmt.Println("I have a", fruit)
}

// If you only need the indices
for i, _ := range fruits {
fmt.Println("Fruit index:", i)
}
// OR more concisely:
for i := range fruits {
fmt.Println("Fruit index:", i)
}

Control Flow in Loops

Go provides two important statements for controlling the flow within loops:

Break

The break statement exits the innermost loop immediately:

go
package main

import "fmt"

func main() {
fmt.Println("Finding the first number divisible by both 3 and 7:")

for i := 1; i <= 100; i++ {
if i%3 == 0 && i%7 == 0 {
fmt.Printf("Found it! %d is divisible by both 3 and 7
", i)
break
}
fmt.Printf("Checked %d
", i)
}
}

This will stop as soon as it finds the first number (21) that's divisible by both 3 and 7.

Continue

The continue statement skips the rest of the current iteration and jumps to the next iteration:

go
package main

import "fmt"

func main() {
fmt.Println("Printing only odd numbers:")

for i := 1; i <= 10; i++ {
if i%2 == 0 {
// Skip even numbers
continue
}
fmt.Println(i)
}
}

Output:

Printing only odd numbers:
1
3
5
7
9

Labeled Loops

Go allows you to label loops and use those labels with break and continue statements to control outer loops:

go
package main

import "fmt"

func main() {
fmt.Println("Multiplication table:")

outer:
for i := 1; i <= 5; i++ {
for j := 1; j <= 5; j++ {
if i*j > 15 {
fmt.Println("Stopping at", i, "*", j, "=", i*j)
break outer // Break out of both loops
}
fmt.Printf("%d * %d = %d
", i, j, i*j)
}
}
}

This example will print the multiplication table but stop completely once the product exceeds 15.

Practical Examples

Example 1: File Processing Line by Line

Here's how you might use a loop to process a file line by line:

go
package main

import (
"bufio"
"fmt"
"os"
"strings"
)

func main() {
// Sample text file content
fileContent := `Hello, World!
This is line 2.
And this is line 3.
#This is a comment
The end.`

// Create a string reader (in a real program, this would be a file)
reader := strings.NewReader(fileContent)
scanner := bufio.NewScanner(reader)

lineNumber := 0

// Scan line by line
for scanner.Scan() {
lineNumber++
line := scanner.Text()

// Skip comment lines
if strings.HasPrefix(line, "#") {
fmt.Printf("Line %d: (skipping comment)
", lineNumber)
continue
}

fmt.Printf("Line %d: %s
", lineNumber, line)
}

if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading input:", err)
}
}

Output:

Line 1: Hello, World!
Line 2: This is line 2.
Line 3: And this is line 3.
Line 4: (skipping comment)
Line 5: The end.

Example 2: Implementing a Simple Retry Mechanism

go
package main

import (
"fmt"
"math/rand"
"time"
)

func main() {
rand.Seed(time.Now().UnixNano())

const maxRetries = 5
success := false

for attempt := 1; attempt <= maxRetries; attempt++ {
fmt.Printf("Attempt %d of %d...
", attempt, maxRetries)

// Simulate an operation that might fail
if rand.Float64() < 0.3 { // 30% chance of success
fmt.Println("Success!")
success = true
break
}

fmt.Println("Failed. Retrying...")

// Add exponential backoff between retries
if attempt < maxRetries {
backoff := time.Duration(attempt * attempt * 100) * time.Millisecond
fmt.Printf("Waiting for %v before next attempt
", backoff)
time.Sleep(backoff)
}
}

if !success {
fmt.Println("Operation failed after maximum number of retries")
}
}

This example shows how to implement a retry mechanism with exponential backoff, which is a common pattern in network operations.

Visual Representation

graph TD A[Start] --> B[Initialize loop variables] B --> C{Check condition} C -->|True| D[Execute loop body] D --> E[Execute post statement] E --> C C -->|False| F[Exit loop] D -->|break| F D -->|continue| E

Best Practices

  1. Keep loops simple - If your loop body is getting too complex, consider extracting some logic into separate functions.
  2. Avoid modifying loop variables inside the loop body (except in the post statement).
  3. Be cautious with infinite loops - Always ensure there's a way to exit.
  4. Prefer range-based loops when iterating over collections.
  5. Use appropriate variable scoping - Variables declared in the init statement are scoped to the loop.

Common Gotchas

Variable Capturing in Closures

One common issue occurs when using variables from loops in closures:

go
package main

import "fmt"

func main() {
functions := make([]func(), 3)

// This will not work as expected!
for i := 0; i < 3; i++ {
functions[i] = func() {
fmt.Println(i) // Captures the variable i, not its value
}
}

// All functions will print the same value (3)
for _, f := range functions {
f()
}

fmt.Println("Fixed version:")

// The correct way: create a new variable in each iteration
functions = make([]func(), 3)
for i := 0; i < 3; i++ {
val := i // Create a new variable
functions[i] = func() {
fmt.Println(val)
}
}

for _, f := range functions {
f()
}
}

The fixed version will print 0, 1, 2 as expected.

Summary

In this tutorial, we've explored Go's versatile for loop in its various forms:

  • Basic three-component loops (like C-style for loops)
  • Condition-only loops (like while loops)
  • Infinite loops with break conditions
  • Range-based loops for collections

We've also covered important control flow statements like break and continue, as well as more advanced concepts like labeled loops.

Go's simplified approach to loops actually makes your code more readable and maintainable once you get used to it. By mastering the for loop in its different forms, you'll be well-equipped to handle any iteration needs in your Go programs.

Exercises

  1. Write a program that prints all even numbers between 1 and 20.
  2. Create a function that finds the largest number in a slice using a for loop.
  3. Write a program that counts the frequency of each word in a string.
  4. Implement a simple "FizzBuzz" program using for loops: Print numbers from 1 to 100, but for multiples of 3 print "Fizz", for multiples of 5 print "Buzz", and for multiples of both print "FizzBuzz".
  5. Write a nested loop that prints a simple pattern of asterisks in the shape of a right triangle.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)