Go Range Gotcha

Whilst learning Go over the last couple of months I have encountered a particular gotcha both myself, by colleagues and in existing codebases. The gotcha is that you cannot mutate slice element values when iterating using range. This is because range makes a copy of the element and any updates to it will not be reflected in the original slice.

type Person struct {
    name string
    id int
}

var persons = []Person{{"Alice", 0}, {"Bob", 0}} 

for i, person := range persons {
    person.id = i + 1
}

fmt.Println(persons) // [{Alice 0} {Bob 0}]

In order to fix this you could use the index of the array in the for range loop. You can then drop the value from the loop as shown below. This clearly calls out the fact we are not using a copy of the object and our intent is to mutate.

type Person struct {
    name string
    id int
}

var persons = []Person{{"Alice"}, {"Bob"}} 

for i := range persons {
    persons[i].id = i + 1
}

fmt.Println(persons) // [{Alice 1} {Bob 2}]