ProgrammingGo backend engineer

Explain how the range mechanism in Go works when dealing with maps and slices. What are the nuances of using loop variables and what errors might one encounter?

Pass interviews with Hintsage AI assistant

Answer

The for ... range loop allows for easy iteration over elements of a slice, map, array, or string.

  • For slices: on each iteration, it returns the index and a copy of the element.
  • For maps: it returns the key and a copy of the value.

Example:

s := []int{1, 2, 3} for i, v := range s { fmt.Println(i, v) } m := map[string]int{"a":1, "b":2} for k, v := range m { fmt.Println(k, v) }

Key Nuance: The variables i, v, k, etc. are reused across all iterations! This often becomes a source of bugs when passing them by reference inside the loop or launching goroutines inside range.

Trap Question

What happens if you start a goroutine inside range over a slice capturing the iteration variable? How can you avoid the error?

Answer: A typical error occurs: inside the goroutine, loop variables are used, which after the loop ends will have the last value. To avoid this — you need to create local copies:

nums := []int{1, 2, 3} for _, v := range nums { go func(val int) { fmt.Println(val) }(v) }

Examples of real errors due to lack of understanding of nuances


Story

In one project, range was used to fill a slice through several goroutines, forgetting to make copies of the loop variables. All goroutines printed the same value — the last one from the array, which severely spoiled the business logic.


Story

When ranging over a map, a reference to the value was saved in a new slice of pointers. As a result, all elements of the new slice referenced the same variable — the one that was used in the loop (a copy of the value). The bug manifested when these variables were updated outside the loop (panic: invalid memory address or unexpected data).


Story

In an internal tool, when iterating over a string with range, I started handlers for significant substrings, but for each iteration, I received offsets in bytes rather than Unicode characters. Result: incorrect handling for Unicode strings, incorrect character slicing.