What does this append puzzle print?
Question 483HardGo 1.22 to 1.25
a := make([]int, 3, 10)
b := append(a, 1)
c := append(a, 2)
fmt.Println(a, b, c)
fmt.Println(len(a), len(b), cap(b))
Output:
[0 0 0] [0 0 0 2] [0 0 0 2]
3 4 10
a has spare capacity, so neither append allocates. Both write into index 3 of the same backing array. b and c are two headers (len 4) over one array, and the second append overwrote the first. a still has length 3, so it shows no change.
If a had been []int{0,0,0} (cap 3), each append would allocate a new array, and you would get [0 0 0 1] and [0 0 0 2]. Whether slices alias depends on capacity, which the calling code usually cannot see. That is why this bug is so hard to track down.
Fixes: always assign append back to the same variable (s = append(s, x)), or copy before you branch (b := append(slices.Clone(a), 1)), or cap the slice with a full slice expression, a[:len(a):len(a)], so the next append must reallocate.
More on Tricky Output & Code-Review Puzzles
- Q481When are deferred call arguments evaluated? What prints?
- Q482What do f(), g(), and h() return?
- 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?
- Q486Array vs slice in range: what prints?
- Q487Why doesn't this loop update the users?