I have included questions as comments in the code.
I see inconsistency here. We do 3 slices here. After the first slice s = s[:0], s should always point to s[:0].
package main
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
printSlice(s)
// Slice the slice to give it zero length.
//1
s = s[:0]
printSlice(s) //after we do this, should not the var s always point to s[:0]?
// Extend its length.
s = s[:4]
printSlice(s) //here, how is this s pointing back to original s? we just changed it to be s = s[:0]
// Drop its first two values.
s = s[2:] //yet here we somehow refer to only the portion from s[:4]
printSlice(s)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v
", len(s), cap(s), s)
}
the output:
len=6 cap=6 [2 3 5 7 11 13]
len=0 cap=6 [] // slice s[:0]
len=4 cap=6 [2 3 5 7] // slice s[:4] - here we refer to the whole array s
len=2 cap=4 [5 7] // slice s[2:] - here we refer not to the whole array s, but to the part we got from s[:4], which is contradiction to s[:0]
Hope it's not too confusing.
A slice is a view of an underlying array. However you re-slice it, it's still the same underlying array. If you take two slices of the same array, they'll both have the same underlying array also, for example: https://play.golang.org/p/T-hYDWeo2TK
initial := []string{"foo", "bar", "baz"}
s1 := initial[:1]
initial[0] = "qux"
fmt.Println(s1)
[qux]
This is explained in great detail with plenty of examples in the Go blog post Go Slices: usage and internals:
A slice is a descriptor of an array segment. It consists of a pointer to the array, the length of the segment, and its capacity (the maximum length of the segment).
So you can keep making different slices with different length/capacity/start index but the same array. They're just different views on the same array.
A slice has three parts: a pointer into an underlying array, a length (how many elements, starting at the pointer, are in this slice), and a capacity (how many elements, starting at the pointer, are valid storage in the underlying array)?
When you re-slice a slice, the pointer will continue to be a pointer into the same array, the length will be whatever you asked for (but not more than the capacity), and the capacity will be decreased as necessary so that the new slice can't address storage beyond what the old one could (in the usual case, the end of the array).
When you do s = s[:0]
, the only change you make is to set s
's length to 0 — it still points to the same storage, and still has a capacity of 6. The underlying array still contains the same values, unless something else changes them.
When you do s = s[:4]
, the only change you make is to set s
's length to 4 — it still points to the same storage, and still has a capacity of 6. The underlying array still has the values it did before, and now you can see them through the slice again.
When you do s = s[2:]
, its pointer is moved forward by two elements, and its length and capacity are decreased by 2. The underlying array is still the same, but you can't use s
to access its first two elements and you can't derive a new slice from s
which can (unless you do something unsafe with a slice header. Once you move forward, you can't go back.
If you wanted you could, as your first step, do s = s[:0:0]
which would reduce s
's length and capacity both to 0, rendering it useless. Since a slice expression can't increase a slice's capacity, it could never be used to access any elements of the underlying array, giving you what you might have expected to happen from the beginning. This can have its uses if you want to pass a view of some data off to some "untrusted" code and ensure that it doesn't access anything outside of a given window.