What does this print? (range over an array vs a slice)
arr := [3]int{1, 2, 3}
for i, v := range arr {
arr[2] = 100
if i == 2 {
fmt.Println("array:", v)
}
}
sl := []int{1, 2, 3}
for i, v := range sl {
sl[2] = 100
if i == 2 {
fmt.Println("slice:", v)
}
}
nums := []int{1, 2, 3}
for _, v := range nums {
nums = append(nums, v)
}
fmt.Println(len(nums))
Output: array: 3, slice: 100, 6.
The range expression is evaluated once, before the loop starts. For an array, that makes a copy of the whole array (when the value variable is used), so writes to arr during the loop don't reach v. For a slice, only the header is copied. The backing array is shared, so the write is visible. Ranging over &arr or arr[:] would print 100.
The loop length is also fixed at the start. Appending during the loop does not make it longer, so there is no infinite loop and it runs exactly 3 times.
Also, v is a copy of each element, so v.Field = x on a slice of structs changes nothing. Use s[i].Field = x. Since Go 1.22 each iteration gets a fresh v, so taking &v in closures or goroutines is safe. It still does not point into the slice.
More on Arrays, Slices, Maps & Strings
- Q46Explain the semantics of the built-in copy. Is it safe with overlapping slices?
- Q47How do arrays differ from slices? Can you convert between them?
- Q49How can subslicing cause memory leaks, and how do you fix them?
- Q50How do you delete elements from a slice efficiently, and what changed with slices.Delete in Go 1.22?
- Q51Why can't you compare slices with ==? How do you check equality of slices, maps, and structs containing them?
- Q52How do you correctly allocate a 2D slice, and what are the performance trade-offs?