切片切片参考

I'm taking the tour on Golang site, and I'm trying to digest one of the examples. It is unclear how it works:

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
", len(s), cap(s), s)
}

The output is:

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]

After the first slice, s = s[:0] the slice length is 0. Then there is another slicing of s = s[:4]. Although the length is 0, this seems to work. But how this happens? Shouldn't the underlaying array be in accessible from s?

What confuses me more is, the next time we slice it, s = s[2:] we slice the old value of s (which is 4 elements) and not the original array.

Can someone shed some lights what is the difference between the two cases?

Leon addressed me to the Go's blog post, where they address exactly my question.

This is the snippet which helped me better understanding this concept:

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).

A slice cannot be grown beyond its capacity. Attempting to do so will cause a runtime panic, just as when indexing outside the bounds of a slice or array. Similarly, slices cannot be re-sliced below zero to access earlier elements in the array.

Slices can be extended if the array has more elements in it, but it can not access elements below 0 of the slice. It's a window to the underlaying array. The blog post explains it in more depth.

A slice is basically a pointer to memory with some additional information:

1) the number of elements currently used and

2) the capacity, i.e. the remaining length it can occupy.

At the start we create a slice with 6 integers, this makes go create the underlying int array with a total size of 6 as well.

here is your memory locations with addresses (content does not matter here)
 *  *  *  *  *  *
[0][1][2][3][4][5]
 ^
 s points to the start of the memory
len(s) = 6
cap(s) = 6

Next we say: make this slice's len be 0, this is the s = s[:0] which takes a sub-slice of s at position 0 with length 0. Note that s[0:0] is the same, you can omit the first 0.

[0][1][2][3][4][5]
 ^
 s still points to the start of the memory
len(s) = 0
cap(s) = 6

Since the capacity is still the same, we might as well make the length 4 by saying s = s[:4].

 *  *  *  *
[0][1][2][3][4][5]
 ^
 s still points to the start of the memory
len(s) = 4
cap(s) = 6

Then we take a sub-slice that does not start at the beginning of the memory by doing s = s[2:].

       *  *
[0][1][2][3][4][5]
       ^
       s now points to the original address plus two!
len(s) = 2
cap(s) = 4