Background:
The for-range construct was introduced in Go as a way to iterate over collections (slices, arrays, maps, strings). Go developers implemented an optimization: on each iteration of the loop, a copy of the value is made instead of using it by reference directly, which can lead to unexpected errors, especially with loop variables.
Problem:
Many make mistakes when trying to take the address of a variable inside range (for example, &v), thinking they get the address of the collection element, but in fact, they get the address of a local variable.
Solution:
In a for-range loop, new copies of the iterator variables (key, value) are created on each iteration. For simple types, this is harmless, but for structs, it leads to surprises when saving a pointer to an element — it will always point to the same variable rather than different elements of the slice.
Example code:
people := []Person{{Name: "Ivan"}, {Name: "Oleg"}} ptrs := make([]*Person, 0) for _, p := range people { ptrs = append(ptrs, &p) // all ptrs will refer to the same p }
Key features:
What happens when saving references to the value variable inside range?
All references will point to the same memory, as value is a temporary variable.
for _, v := range someSlice { ptrs = append(ptrs, &v) } // All ptrs hold a reference to the same variable!
Can you change an element of the collection by reference through value in range?
No, changing value does not affect the original element of the collection. To change it, you need to reference it by index.
for _, v := range arr { v.Field = 10 // arr won't change } for i := range arr { arr[i].Field = 10 // correct }
Does for-range over a map guarantee sequential order of traversal?
No, the iteration order over a map in Go is not defined and can differ in each run of the application.
A developer tries to serialize a list of pointers to the elements of a struct through range and saves &value in a separate slice. The result is a slice of identical addresses.
Pros:
Cons:
Iterate by index and save a pointer to the required element of the array:
for i := range arr { ptrs = append(ptrs, &arr[i]) }
Pros:
Cons: