Loop
Unlike many traditional languages that rely on for, while, and do-while structures, Go simplifies everything. It uses only one looping keyword: for. This single keyword is incredibly flexible, capable of handling traditional counting, endless background tasks, and elegant collection iteration. Mastering its three core variations is fundamental to writing idiomatic Go.
The Standard: Traditional Three-Component Loop
The most recognizable form of the for loop is the one borrowed directly from C, but with a critical Go difference: you omit the parentheses.
This structure has three optional components, separated by semicolons:
- Initialization Statement (Init): Executed once before the loop starts (e.g., setting a counter variable).
- Condition Expression (Condition): Evaluated before each iteration. The loop continues only if this evaluates to
true. - Post Statement (Post): Executed after the body of the loop has finished (e.g., incrementing the counter).
Syntax Example (Counting to Five)
package main
import "fmt"
func main() {
// 1. Init: i := 1
// 2. Condition: i <= 5
// 3. Post: i++
for i := 1; i <= 5; i++ {
fmt.Printf("Traditional Count: %d\n", i)
}
}
Insight: The variable initialized in the Init statement (i in the example) is scoped only to the for loop block. It cannot be accessed outside the loop.
Variation 1: Simulating the while Loop
What if you don't need a counter, but just need to loop as long as a certain external condition remains true? In languages like Python or Java, you'd use while. In Go, you simply omit the Initialization and Post statements.
If you only provide the Condition expression, the Go for loop behaves exactly like a while loop.
Syntax Example (Simple While Simulation)
package main
import "fmt"
func main() {
countdown := 3
// Only the Condition component is present
for countdown > 0 {
fmt.Printf("T-minus %d...\n", countdown)
countdown-- // The decrement happens inside the body, like a while loop
}
fmt.Println("Lift off!")
}
Practical Use: This style is perfect for loops where the exit condition is complex, depends on user input, or involves waiting for an asynchronous process to complete (e.g., polling a queue).
Variation 2: The Infinite Loop (Service Workers)
If you omit all three components (Init, Condition, and Post), you create an infinite loop. This is extremely common and essential in Go programs, especially for server processes, background workers, or routines that handle continuous incoming data.
Syntax Example (Basic Infinite Loop)
package main
import (
"fmt"
"time"
)
func main() {
// This loop runs forever unless explicitly stopped
for {
fmt.Println("Worker processing task...")
// Simulate work being done
time.Sleep(1 * time.Second)
// Important: Use a 'break' or 'return' statement
// if you need an escape mechanism based on some internal logic.
if time.Now().Hour() > 18 {
fmt.Println("Exiting worker for the day.")
break
}
}
}
Warning: An infinite loop without a break statement or an external signal (like a channel closing or a panic) will run indefinitely, potentially locking up the goroutine or the entire program if not managed correctly.
Variation 3: Iterating Collections with range
The final, and arguably most idiomatic, form of the Go for loop is the for range construct. This structure is specifically designed to iterate over collections like:
- Slices
- Arrays
- Strings
- Maps
- Channels
This makes iteration safe, clean, and expressive.
What range Returns
- Slices & Arrays: Index and value
- Maps: Key and value
- Strings: Byte index and Unicode rune (character)
Example A: Iterating a Slice (Index and Value)
package main
import "fmt"
func main() {
items := []string{"Apple", "Banana", "Cherry"}
for index, fruit := range items {
fmt.Printf("Item %d is %s\n", index, fruit)
}
}
Example B: Iterating a Map (Key and Value)
package main
import "fmt"
func main() {
scores := map[string]int{"Alice": 95, "Bob": 82, "Charlie": 99}
for name, score := range scores {
fmt.Printf("%s scored %d points\n", name, score)
}
}
Handling Unused Range Variables (The Blank Identifier)
Go demands that all declared variables be used. If you iterate a collection but only need one of the values, you must use the blank identifier (_) to discard the unused variable.
Only Needing the Value
// Iterating a slice, discarding the index
for _, fruit := range items {
fmt.Printf("We have %s\n", fruit)
}
Only Needing the Key / Index
// Iterating a map, discarding the score
for name, _ := range scores {
fmt.Printf("Student name: %s\n", name)
}
// Or even simpler: omit the second variable entirely for slices/arrays
for index := range items {
fmt.Printf("Index used: %d\n", index)
}
Summary: Three Forms, One Keyword
The power of Go's simplicity is evident in the single for loop keyword. By mastering how to strategically omit its components, you unlock four distinct and highly useful looping patterns:
| Pattern | Syntax | Analogy | Use Case |
|---|---|---|---|
| Traditional | for init; condition; post {} | C-style for | Fixed iterations, counting, numerical boundaries |
| Conditional | for condition {} | while loop | Looping until a specific state is reached |
| Iteration | for key, value := range collection {} | Iterator | Safely iterating slices, maps, strings, and channels |
| Infinite | for {} | while (true) | Background services, servers, continuous workers |