去切片-[:n]和[n:]之间的区别

Go Slice question, please check below and comment if I am missing something.

   import "fmt"
   func main() {
       s := []int{2, 3, 5, 7, 11, 13}
       s = s[1:]
       fmt.Println(s)
       s = s[2:]
       fmt.Println(s)
       s = s[5:]
       fmt.Println(s)
  }

Output:

[3 5 7 11 13] [7 11 13] panic: runtime error: slice bounds out of range

The above makes sense.

func main() {
       s := []int{2, 3, 5, 7, 11, 13}
       s = s[:1]
       fmt.Println(s)
       s = s[:2]
       fmt.Println(s)
       s = s[:5]
       fmt.Println(s)
   }

Output:

[2] [2 3] [2 3 5 7 11]

Should this also get array out of bounds panic from s=s[:2]?

Subslicing in Go allows you to slice beyond the end of the slice, as long as it's still within range of the underlaying array's capacity. You cannot slice before the start of that slice, but you can slice after it so long as you don't go past that last allocated index.

As an example, s[3:] then s[:3] works, but s[4:] then s[:4] will panic, as you're requesting indexes 4 through 7 of the underlying array, which only has allocated indexes 0-5.

It's a bit of an oddity, but it does allow you to max out any slice simply by doing slice = slice[:cap(slice)].

https://play.golang.org/p/Gq5xoXc3Vd

The language specification annotes this, btw. I've paraphrased it below for the simple slice notation you're using (there's an alternative that also specifies the maximum index for the new slice).

For a string, array, pointer to array, or slice a, the primary expression a[low : high] constructs a substring or slice. The indices are in range if 0 <= low <= high <= cap(a), otherwise they are out of range.

I just wanted to share my thoughts on this after reading the official blog: https://blog.golang.org/slices

Here is the sliceHeader in Golang's blog:

type sliceHeader struct {
    Length        int
    ZerothElement *byte
}

Now you declare your slice:

s := []int{2, 3, 5, 7, 11, 13}

which I believe does something like:

var array [6]int
slice := sliceHeader {
    Length:        6,
    ZerothElement: &array[0], 
}

By doing: s = s[:2], you effectively changed the Length of the sliceHeader from 6 to 2, so your sliceHeader will look like this:

slice := sliceHeader {
    Length:        2,
    ZerothElement: &array[0],
}

Notice that the ZerothElement is still pointing to the same place in memory. So we can extend the slice back to its original form s = s[:6] by simply changing the Length again.

Now, let's say you did not do s = s[:2] and instead did s = s[2:], what you have effectively done is hiding the first two elements by subtracting 2 from Length AND shifting the ZerothElement two indices forward, resulting in a sliceHeader of:

slice := sliceHeader {
    Length:        4,
    ZerothElement: &array[2],   
}

At this point, you CANNOT restore the slice to its original form because there is no way to extend the slice beyond the ZerothElement. Okay, what if hypothetically you could access whatever elements before the ZerothElement? Then our slice becomes undefined because it could be array[0...4], array[1...5], or array[2...6].

So yeah, that is why I think [n:] and [:n] behaves differently.