Question

Go foreach Loop: How to Iterate with for range in Go

goforeachslice

Question

In Go, is there a foreach construct similar to other languages?

Can I iterate over a slice or array using a for loop, and if so, what is the idiomatic way to do it?

Short Answer

By the end of this page, you will understand how Go handles foreach-style iteration using for and range. You will see how to loop through slices and arrays, when to use indexes and values, and how this pattern is commonly used in real Go programs.

Concept

Go does not have a separate foreach keyword.

Instead, Go uses a single loop keyword: for. To write a foreach-style loop, you combine for with range.

This is the idiomatic Go way to iterate over collections such as:

  • slices
  • arrays
  • maps
  • strings
  • channels

For slices and arrays, range gives you:

  • the current index
  • a copy of the current element value

Example:

numbers := []int{10, 20, 30}

for index, value := range numbers {
    fmt.Println(index, value)
}

Why this matters:

  • It is shorter and clearer than manually managing a counter.
  • It reduces common indexing mistakes.
  • It is the standard style used across Go codebases.

Go keeps the language small by reusing for for all looping needs instead of adding separate constructs like while and foreach.

Mental Model

Think of range as a helper that walks through a collection and hands you information for each step.

Imagine a row of labeled boxes:

  • the label is the index
  • the item inside is the value

When you write:

for i, v := range items {
    // use i and v
}

Go is effectively saying:

  • "Here is box 0 and its value"
  • "Here is box 1 and its value"
  • "Here is box 2 and its value"

You can choose to use both the label and the item, or ignore one of them if you do not need it.

Take Quiz

Syntax and Examples

Basic syntax

for index, value := range collection {
    // use index and value
}

Iterate over a slice

package main

import "fmt"

func main() {
    fruits := []string{"apple", "banana", "orange"}

    for i, fruit := range fruits {
        fmt.Println(i, fruit)
    }
}

Output:

0 apple
1 banana
2 orange

Explanation:

  • i is the index
  • fruit is the value at that index

Ignore the index

If you only need the value, use _ for the index:

for _, fruit := range fruits {
    fmt.Println(fruit)
}

Use only the index

If you only need positions:

for i := range fruits {
    fmt.Println(i)
}

Classic indexed loop

You can also use a regular for loop:

for i := 0; i < len(fruits); i++ {
    fmt.Println(i, fruits[i])
}

This is useful when:

  • you need full control over the index
  • you want to skip or step manually
  • you want to modify elements by index

Step by Step Execution

Consider this code:

package main

import "fmt"

func main() {
    scores := []int{90, 85, 100}

    for i, score := range scores {
        fmt.Println("index:", i, "value:", score)
    }
}

Step-by-step

  1. scores is created as a slice with three values: 90, 85, and 100.
  2. The range scores part starts iterating over the slice.
  3. On the first loop:
    • i becomes 0
    • score becomes 90
    • Go prints index: 0 value: 90
  4. On the second loop:
    • i becomes 1
    • score becomes 85
    • Go prints index: 1 value: 85
  5. On the third loop:
    • i becomes 2
    • score becomes 100
    • Go prints index: 2 value: 100
  6. The loop ends after the last element.

Output:

index: 0 value: 90
index: 1 value: 85
index: 2 value: 100

The key idea is that range automatically moves through the slice for you.

Real World Use Cases

Foreach-style iteration is used constantly in Go programs.

Common examples

Processing API results

for _, user := range users {
    fmt.Println(user.Name)
}

Use this after decoding JSON into a slice of structs.

Validating input data

for _, email := range emails {
    if email == "" {
        fmt.Println("empty email found")
    }
}

Useful when checking form input or imported CSV rows.

Building derived data

total := 0
for _, price := range prices {
    total += price
}

Common in reports, analytics, and billing logic.

Updating elements by index

for i := range prices {
    prices[i] = prices[i] * 2
}

This pattern matters when you need to modify the underlying slice.

Reading maps

for key, value := range settings {
    fmt.Println(key, value)
}

Used in configuration handling and metadata processing.

Take Quiz

Real Codebase Usage

In real Go codebases, for range is often combined with simple control-flow patterns.

Common patterns

Validation loops

for _, item := range items {
    if item.ID == 0 {
        return fmt.Errorf("invalid item id")
    }
}

Developers often stop early when bad data is found.

Filtering data

var activeUsers []User
for _, user := range users {
    if user.Active {
        activeUsers = append(activeUsers, user)
    }
}

Go does not have built-in filter, so loops are the normal way.

Aggregation

count := 0
for _, order := range orders {
    count += order.Quantity
}

Used for totals, summaries, and statistics.

Updating by index

for i := range users {
    users[i].Active = true
}

This is important because ranging over values gives you a copy of each element.

Guard clauses inside loops

for _, file := range files {
    if file == "" {
        continue
    }
    fmt.Println(file)
}

continue skips unwanted items cleanly.

In practice, Go developers prefer straightforward loops over abstract iteration helpers because the code is easy to read and debug.

Common Mistakes

1. Expecting a foreach keyword

Broken expectation:

// This is not valid Go
foreach item in items {
    fmt.Println(item)
}

Use this instead:

for _, item := range items {
    fmt.Println(item)
}

2. Forgetting to use _ for unused values

Broken code:

for i, fruit := range fruits {
    fmt.Println(fruit)
}

If i is never used, Go will report an error.

Correct version:

for _, fruit := range fruits {
    fmt.Println(fruit)
}

3. Trying to modify the value variable and expecting the slice to change

Broken assumption:

numbers := []int{1, 2, 3}
for _, n := range numbers {
    n = n * 10
}
fmt.Println(numbers)

Output remains unchanged:

[1 2 3]

Why: n is a copy of the element.

Correct version:

for i := range numbers {
    numbers[i] = numbers[i] * 10
}

4. Using the wrong loop style when index access is needed

If you need to write back into the slice, prefer index-based access.

for i := range numbers {
    numbers[i] += 1
}

5. Assuming map iteration order is fixed

for k, v := range myMap {
    fmt.Println(k, v)
}

Map iteration order in Go is not guaranteed. Do not rely on it for sorted output.

Comparisons

ApproachSyntaxBest forNotes
for _, v := range sliceForeach-styleReading valuesMost idiomatic for simple iteration
for i := range sliceIndex-onlyModifying elements by indexGood when writing back to the slice
for i, v := range sliceIndex and valueReading both position and itemCommon and readable
for i := 0; i < len(slice); i++Classic loopFull controlUseful for custom stepping or paired indexing

range vs classic for

  • Use range when you want clean, direct iteration.
  • Use classic for when you need:
    • custom increments
    • reverse looping
    • complex index logic

Go vs languages with foreach

Language styleExample ideaGo equivalent
C# foreachiterate values directlyfor _, v := range items
Python for item in itemsiterate values directlyfor _, v := range items
Java enhanced forforeach over arrays/listsfor _, v := range items

Go chooses not to add a separate foreach keyword because for already covers all loop types.

Take Quiz

Cheat Sheet

Foreach in Go

There is no foreach keyword in Go.

Use:

for _, value := range collection {
    // use value
}

Common patterns

Index and value

for i, v := range items {
}

Value only

for _, v := range items {
}

Index only

for i := range items {
}

Classic loop

for i := 0; i < len(items); i++ {
}

Important rules

  • range works with slices, arrays, maps, strings, and channels.
  • For slices and arrays, range returns index and value.
  • The value variable is usually a copy.
  • Use _ to ignore unused values.
  • To modify slice elements, use the index.
  • Map iteration order is not guaranteed.

Quick example

nums := []int{1, 2, 3}
for i, n := range nums {
    fmt.Println(i, n)
}

FAQ

Is there a foreach loop in Go?

No. Go does not have a separate foreach keyword. The idiomatic equivalent is for ... range.

How do I loop over a slice in Go?

Use:

for _, v := range slice {
    fmt.Println(v)
}

How do I get both index and value in a Go loop?

Use:

for i, v := range slice {
    fmt.Println(i, v)
}

How do I change slice elements while looping?

Loop by index:

for i := range slice {
    slice[i] = slice[i] * 2
}

Why does Go use _ in loops?

_ discards a value you do not need. This is useful because Go does not allow unused local variables.

Is range only for slices and arrays?

No. It also works with maps, strings, and channels.

Should I use range or a classic for loop?

Use range for most collection iteration. Use classic for when you need special index control, such as reverse loops or custom step sizes.

Take Quiz

Mini Project

Description

Build a small Go program that processes a list of product prices. This project demonstrates foreach-style iteration with for range, summing values, printing each item, and modifying a slice by index when needed.

Goal

Create a Go program that prints product prices, calculates the total, and then applies a discount by updating each price in the slice.

Requirements

  • Create a slice of numeric prices.
  • Use for range to print each price.
  • Use for range to calculate the total price.
  • Update each element in the slice to apply a discount.
  • Print the updated slice after modification.
Take Quiz

Keep learning

Related questions

View more