了解Go的切片符号

I'm going through the Go-tour and this module has got me thinking, it seems that every-time you modify the view by slicing the lower bound, the capacity and length of the slice is reduced. However as taught earlier, the underlying array created by the slice does not get altered.

I simplified the example code down to this:

package main

import "fmt"

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

    s = s[6:]
    fmt.Println(cap(s), len(s), s)
}

This prints: 0 0 [] meaning this slice is now totally useless.

However, the array is un-altered, the array is still (for the sake of visualization):

[2, 3, 5, 7, 11, 13]

and referenced by s, meaning it won't get garbage collected.

So my question is, is this a side effect of slices or is this expected / preferred behavior? and secondly, is there a way to restore the view back to the original? (showing [2, 3, 5, 7, 11, 13])

You seem to understand Go slices.


A Go slice is implemented as a struct:

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

It's a view into the underlying array.

For example,

package main

import "fmt"

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

    t := s[cap(s):]
    fmt.Println("s", cap(s), len(s), s)
    fmt.Println("t", cap(t), len(t), t)

    t = s
    fmt.Println("s", cap(s), len(s), s)
    fmt.Println("t", cap(t), len(t), t)
}

Playground: https://play.golang.org/p/i-gufiJB-sP

Output:

s 6 6 [2 3 5 7 11 13]
s 6 6 [2 3 5 7 11 13]
t 0 0 []
s 6 6 [2 3 5 7 11 13]
t 6 6 [2 3 5 7 11 13]

The underlying array will not be garbage collected until there are no references (pointers) to any element of the underlying array.

For example,

package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    fmt.Println("s", cap(s), len(s), s, &s[0])
    t := s
    // the slice s struct can be garbage collected
    // the slice s underlying array can not be garbage collected
    fmt.Println("t", cap(t), len(t), s, &t[0])
    p := &t[0]
    // the slice t struct can be garbage collected
    // the slice t (slice s) underlying array can not be garbage collected
    fmt.Println("p", p, *p)
    // the pointer p can be garbage collected
    // the slice t (and s) underlying array can be garbage collected
}

Playground: https://play.golang.org/p/PcB_IS7S3QE

Output:

s 6 6 [2 3 5 7 11 13] 0x10458000
t 6 6 [2 3 5 7 11 13] 0x10458000
p 0x10458000 2

Read:

The Go Blog: Go Slices: usage and internals

The Go Blog: Arrays, slices (and strings): The mechanics of 'append'

The Go Programming Language Specification : Slice types and Slice expressions

As about your second question, yes, you can restore original but only with very dark magic of reflect and unsafe.

hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s)) //extract SliceHeader
arr := *(*[6]int)(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&s)).Data)) //restore array data

playground

It cannot be used safely or portably and its representation may change in a later release.

Do not use this in production.