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
0and its value" - "Here is box
1and its value" - "Here is box
2and its value"
You can choose to use both the label and the item, or ignore one of them if you do not need it.
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:
iis the indexfruitis 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
scoresis created as a slice with three values:90,85, and100.- The
range scorespart starts iterating over the slice. - On the first loop:
ibecomes0scorebecomes90- Go prints
index: 0 value: 90
- On the second loop:
ibecomes1scorebecomes85- Go prints
index: 1 value: 85
- On the third loop:
ibecomes2scorebecomes100- Go prints
index: 2 value: 100
- 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.
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
| Approach | Syntax | Best for | Notes |
|---|---|---|---|
for _, v := range slice | Foreach-style | Reading values | Most idiomatic for simple iteration |
for i := range slice | Index-only | Modifying elements by index | Good when writing back to the slice |
for i, v := range slice | Index and value | Reading both position and item | Common and readable |
for i := 0; i < len(slice); i++ | Classic loop | Full control | Useful for custom stepping or paired indexing |
range vs classic for
- Use
rangewhen you want clean, direct iteration. - Use classic
forwhen you need:- custom increments
- reverse looping
- complex index logic
Go vs languages with foreach
| Language style | Example idea | Go equivalent |
|---|---|---|
C# foreach | iterate values directly | for _, v := range items |
Python for item in items | iterate values directly | for _, v := range items |
Java enhanced for | foreach over arrays/lists | for _, v := range items |
Go chooses not to add a separate foreach keyword because for already covers all loop types.
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
rangeworks with slices, arrays, maps, strings, and channels.- For slices and arrays,
rangereturns 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.
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 rangeto print each price. - Use
for rangeto calculate the total price. - Update each element in the slice to apply a discount.
- Print the updated slice after modification.
Keep learning
Related questions
Are Go Maps Passed by Value or Reference?
Learn Go map parameter and return semantics: maps are passed by value, while copies share underlying data. Know when map pointers are useful.
Authentication for Go REST APIs: Sessions, Tokens, and Middleware
Learn practical authentication patterns for Go REST APIs, including password hashing, server sessions, bearer tokens, middleware, and security rules.
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.