From "A Tour of Go" - Why this slice changes its capacity?

Remember that slice holds data in an array. By dropping first two elements we moved the beginning of the slice to the right and now there are fewer slots between the start of the slice inside the array and the end of the array.

Droping elements at the end of the slice has no effect on capacity as the distance between start of the slice inside the array and the end of the backing array does not change.

Neither of the operations modify the backing array, they just modify the slice data.

See https://blog.golang.org/go-slices-usage-and-internals the observed behaviour is explained in section Slice internals

By printing the slice header you can see the changes happening

func printSlice(s []int) {
    sh := (*reflect.SliceHeader)(unsafe.Pointer(&s))
    fmt.Printf("header=%+v len=%d cap=%d %v\n", sh, len(s), cap(s), s)
}

In the last call, the data pointer is moved ahead.

header=&{Data:272990208 Len:6 Cap:6} len=6 cap=6 [2 3 5 7 11 13]
header=&{Data:272990208 Len:0 Cap:6} len=0 cap=6 []
header=&{Data:272990208 Len:4 Cap:6} len=4 cap=6 [2 3 5 7]
header=&{Data:272990216 Len:2 Cap:4} len=2 cap=4 [5 7]

Basically a slice has 3 elements : pointer to data, length and capacity. The pointer to data is a reference to an underlying array (fixed size, contiguous allocation).

One can take any subset of this slice to form another slice. When one selects a subset 0 to m (m > 0) elements from the left of a slice sized N (m < N), then one has items 0 to m in the new slice and at the same time the entire underlying array capacity available (as the pointer to data still points to location 0). So one can expand it till N without any change in capacity.

However if one slices m (m > 0) to k (k < N), then the pointer to data is now pointing to location of m and the new length is from m to k - but now we only have capacity m to N. We have lost access of elements 0 to m in the new slice forever. Once we understand that it's just a pointer manipulation similar to other languages/architectures, it's fairly simple. Hope this helps.


This go blog clarifies this point by explaining that the array consists of actual values and the slice in contrast contains a pointer to the array, more specifically that pointer points to an element of the array: "The capacity is the number of elements in the underlying array (beginning at the element referred to by the slice pointer)"

Tags:

Go