ProgrammingMiddle/Senior Go backend developer

How do for-range loops work with slices and maps in Go and what value copying issues arise when using them?

Pass interviews with Hintsage AI assistant

Answer.

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:

  • New copies of the key/value variables are created in each loop iteration.
  • Taking the address of a value inside for-range returns not the address of the element in the collection, but the address of a temporary variable.
  • For maps, the iteration occurs in random order, there is no guarantee of sequence.

Trick questions.

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.

Common mistakes and anti-patterns

  • Using &value in range to save references to different elements.
  • Modifying the value variable instead of referencing by index.
  • Expecting sequential order of traversal over a map.

Real-life example

Negative case

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:

  • Conciseness of the loop.

Cons:

  • All references are the same, changing one element changes "all".

Positive case

Iterate by index and save a pointer to the required element of the array:

for i := range arr { ptrs = append(ptrs, &arr[i]) }

Pros:

  • Each pointer points to a separate element of the original slice.

Cons:

  • The syntax is longer, but the result is predictable.