# Why for and range Matter
Go has one loop keyword: for. That simplicity is intentional. Instead of teaching multiple looping constructs, Go gives you one consistent control-flow tool and extends it with range.
In real services, loops are everywhere: processing API inputs, scanning DB rows, iterating map-based caches, consuming channels, and parsing text. Understanding exactly what range returns and copies prevents subtle bugs.
Real-World Analogyclick to expand
Think of for as an assembly line conveyor and range as the worker that hands you each item. For slices, you get (position, item). For maps, you get (key, value). For channels, you get one item until supply ends.
If you misunderstand what is being handed (a copy vs original), you can update the wrong thing — like editing a photocopy and expecting the original to change.
# The Three Forms of for
With one keyword, you can express classic loops, while-style loops, and infinite loops.
// 1) C-style for
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// 2) While-style
n := 1
for n < 100 {
n *= 2
}
// 3) Infinite loop
for {
// work
break
}# range Across Types
range works differently depending on the collection type. Learn the returned values:
- Slice/array:
index, value - Map:
key, value(order not guaranteed) - String:
byteIndex, rune - Channel:
valueuntil closed
nums := []int{10, 20, 30}
for i, v := range nums {
fmt.Printf("slice[%d]=%d
", i, v)
}
m := map[string]int{"a": 1, "b": 2}
for k, v := range m {
fmt.Printf("map[%s]=%d
", k, v)
}
for pos, r := range "héy" {
fmt.Printf("byte=%d rune=%c
", pos, r)
}# Step-by-Step Gotchas and Fixes
`v` is a copy in `for _, v := range slice`Updating v does not update the original slice. Use index when you need in-place mutation:for i := range nums { nums[i] *= 2 }.
Map iteration order is randomNever assume insertion order when ranging over maps. If ordering matters, copy keys to a slice and sort before lookup.
Appending during range can surprise youThe range expression is evaluated once. Appended elements are not revisited in that same loop.
# break, continue, and Labels
Use break to exit, continue to skip current iteration, and labels to control outer loops.
// continue: skip evens
for i := 0; i < 6; i++ {
if i%2 == 0 {
continue
}
fmt.Println(i) // 1 3 5
}
// labeled break: exit nested loops
outer:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
break outer
}
fmt.Println(i, j)
}
}# Real-World Example: Batch Processor
This pattern appears in API workers: iterate requests, skip invalid input, aggregate valid results.
package main
import "fmt"
type Job struct {
ID int
Valid bool
Cost int
}
func TotalCost(jobs []Job) int {
total := 0
for _, job := range jobs {
if !job.Valid {
continue
}
total += job.Cost
}
return total
}
func main() {
jobs := []Job{{1, true, 10}, {2, false, 99}, {3, true, 20}}
fmt.Println(TotalCost(jobs)) // 30
}⚡ Key Takeaways
foris Go's only loop and covers classic, while-style, and infinite loopsrangereturns different values by type (slice, map, string, channel)- In slice loops, value variable is a copy; mutate with index when needed
- Map iteration order is not guaranteed; sort keys when deterministic order matters
- Use
continue,break, and labels for clear loop control