Array vs slice in range: what prints?
Question 486HardGo 1.22 to 1.25
arr := [3]int{1, 2, 3}
for i, v := range arr {
if i == 0 {
arr[1] = 100
}
fmt.Print(v, " ")
}
fmt.Println(arr)
sl := []int{1, 2, 3}
for i, v := range sl {
if i == 0 {
sl[1] = 100
sl = append(sl, 4)
}
fmt.Print(v, " ")
}
fmt.Println(sl)
Output:
1 2 3 [1 100 3]
1 100 3 [1 100 3 4]
The range expression is evaluated once, before the loop starts. For an array, the value is copied, so iteration reads the copy and never sees arr[1] = 100. Use range &arr or arr[:] to iterate the original without a copy.
For a slice, only the header is copied. The element write is visible because the backing array is shared, so the loop prints 100. The length is fixed at 3, though, so the appended element is never visited. Even if append had reallocated, the loop would keep reading the old array. Ranging over a large array value also copies the whole thing, which is a performance trap.
More on Tricky Output & Code-Review Puzzles
- Q484What does the caller see after calling modify?
- Q485Code review: what is wrong with returning a small sub-slice of a large buffer, and what does s[low:high:max] fix?
- Q487Why doesn't this loop update the users?
- 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?