In this slide of A Tour of Go, there is this snippet:
package main
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
printSlice(s)
// Slice the slice to give it zero length.
s = s[:0]
printSlice(s)
// Extend its length.
s = s[:4]
printSlice(s)
// Drop its first two values.
s = s[2:]
printSlice(s)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
and the result:
len=6 cap=6 [2 3 5 7 11 13]
len=0 cap=6 []
len=4 cap=6 [2 3 5 7]
len=2 cap=4 [5 7]
My confusion is about the cap=4
in the last line - I thought it should remain 6.
What is the cause of the capacity changing from 6 to 4 in the last line?
Also, why only the last line changes its capacity but the others don't ?
A Tour of Go is an introduction to the Go programming language. Visit https://tour.golang.org to start the tour.
Go (also called Golang or Go language) is an open source programming language used for general purpose. Go was developed by Google engineers to create dependable and efficient software. Most similarly modeled after C, Go is statically typed and explicit.
What is Golang Useful for? Golang is useful for carrying out programming for scalable servers and large software systems. The Golang programming language was built to fill in the gaps of C++ and Java that Google came across while working with its servers and distributed systems.
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 :
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 (all that changes is the pointer to data, start of the 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 start of 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 in the new slice. 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With