Why doesn't this loop update the users?
Question 487MediumGo 1.22 to 1.25
type User struct {
Name string
Age int
}
users := []User{{"ann", 30}, {"bob", 40}}
for _, u := range users {
u.Age++
}
fmt.Println(users) // [{ann 30} {bob 40}]
u is a copy of each element. Incrementing it changes the copy, which is thrown away at the end of the iteration. Go 1.22's per-iteration variables do not change this. Each iteration just gets its own copy.
Fixes:
for i := range users {
users[i].Age++ // index into the slice
}
for i := range users {
u := &users[i] // pointer to the element
u.Age++
}
A slice of pointers ([]*User) also works, because the copied value is then a pointer. Code-review angle: with large structs, for _, u := range also copies every element on every iteration, which costs CPU. Linters such as gocritic rangeValCopy flag it. Maps have the same issue, and the fix is worse: you cannot take the address of a map element (&m[k] does not compile), so you must read, modify, and write back with m[k] = u.
More on Tricky Output & Code-Review Puzzles
- Q485Code review: what is wrong with returning a small sub-slice of a large buffer, and what does s[low:high:max] fix?
- Q486Array vs slice in range: what prints?
- Q488Why is err != nil true here even though the function returned a nil pointer?
- Q489Why doesn't this compile, and why does Go refuse it?
- Q490When is the receiver bound for a method value? What prints?
- Q491Code review: why does Counter always report 0, and what else is wrong?