This puzzle comes from go-traps.appspot.com:
package main
import "fmt"
func main() {
a := make([]int, 3, 4)
a[0], a[1], a[2] = 0, 1, 2
b := append(a, 66)
b[0] = 6
c := append(a, 77)
c[0] = 7
d := append(a, 88, 99)
d[0] = 9
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
fmt.Println(d)
}
I think this is a good tutorial to explain the nitty-gritty behind the slice in Go
, so I will analyze this issue detailedly. But before our journey, let’s get into some preliminaries of slice first:
The internals of a slice is like the following diagram:
A slice consists of 3
components:
a) ptr: A pointer which points to the start position of slice in the underlying array;
b) len (also known as length, and its type is int
): the number of the valid elements of the slice;
b) cap (also known as capacity, and its type is int
): the total number of slots of the slice.
Now that we have known the construction of the slice, we can explain the problem step by step:
(1)
a := make([]int, 3, 4)
a[0], a[1], a[2] = 0, 1, 2
The above statements are very general. Now the a
slice is as follows:
(2)
b := append(a, 66)
Since the capacity of a
is 4
, and the length of a
is 3
, so a
has a free slot for storing the new element: 66
. Besides this, the operation of “append(a, 66)
” assigns to a new variable: b
, so the value of a
doesn’t change: len
is still 3
; cap
is still 4
; ptr
still points the original array, but the content of original array is modified: the 4th
element is 66
now. Because b
is the assignee of “append(a, 66)
“, it points to the same underlying array of a
, but The len
of b
is 4
:
After running “b[0] = 6
“, now the memory layout is like this:
(3)
c := append(a, 77)
c[0] = 7
Since the len
of a
is 3
up to this time, the program will consider there is an available slot. The result is the 4th
element in the array will be changed to 77
from 66
. “c[0] = 7
” will modify the 1st
element. Now the ptr
s of a
, b
and c
all point to the same array:
(4)
d := append(a, 88, 99)
d[0] = 9
Since a
only has 1
accessible slot, “append(a, 88, 99)
” will relocating a new array (the size will be doubled to be be more efficient, that is from 4
to 8
in this case.). So now the ptr
of d
points to a new array, and the final result should be here:
To wrap out this discussion, I want to quote the following statement from go-traps.appspot.com:
A good rule of thumb for non-specialists would be “never call append without assigning back to the same variable”.
So if you really need using append
but without assigning back the original value, please be more careful, or else it may bite you and give you a big unpleasant surprise!