>_
GolangStepByStep
Intern

For & Range

Go has one loop keyword — master all its forms including range

# 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: value until 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 random

Never assume insertion order when ranging over maps. If ordering matters, copy keys to a slice and sort before lookup.

Appending during range can surprise you

The 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

  • for is Go's only loop and covers classic, while-style, and infinite loops
  • range returns 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
practice & review