为什么golang slices内部设计成这样?

Code:

func main() {

    a := []int{1, 2}
    printSlice("a", a)

    b := a[0:1]
    printSlice("b origin", b)

    b = append(b, 9)
    printSlice("b after append b without growing capacity", b)
    printSlice("a after append b without growing capacity", a)

    b = append(b, 5, 7, 8)
    printSlice("a after append b with grown capacity", a)
    printSlice("b after append b with grown capacity", b)

    b[0] = 1000
    printSlice("b", b)
    printSlice("a", a)      

}

func printSlice(s string, x []int) {
    fmt.Printf("%s len=%d cap=%d %v
",
        s, len(x), cap(x), x)
}

Output:

a len=2 cap=2 [1 2]
b origin len=1 cap=2 [1]
b after append b without growing capacity len=2 cap=2 [1 9]
a after append b without growing capacity len=2 cap=2 [1 9]
a after append b with grown capacity len=2 cap=2 [1 9]
b after append b with grown capacity len=5 cap=6 [1 9 5 7 8]
b len=5 cap=6 [1000 9 5 7 8]
a len=2 cap=2 [1 9]

The interesting thing is at the last two printed lines. I already know that a slice is just a window of underlying array. When reslicing it within is capacity, then the two slices share the same underlying array, but When I reslice it to grow beyond its capaccity, the two slices have distinct underlying array. But why golang designers choose not to change the underlying array of the origin slice to the underlying array of the new slice, so as to make both slices still have the same underlying array? In current state when I changed the value of some elements of newly resliced slice I have to check if I changed the underlying array to decide if this operation have side effects on other slices backed up by it(see the last two lines of Output). I think it's awkward.

But why golang designers choose not to change the underlying array of the origin slice to the underlying array of the new slice, so as to make both slices still have the same underlying array?

Mainly, slices of the same array can appear absolutely anywhere in the program--completely different functions, packages, and so on. Given how slices are laid out in memory, Go would have to "find" all slices sharing the array to update them; it has no way to.

The approach of some other array-list implementations (like Python lists) is that what you pass around is really a pointer to something like a Go slice, and if two variables hold "the same list", an append using one variable will also show up when you look at the other. That also has some efficiency cost--another pointer lookup to do a[0]. In those circumstances where you really need an append over here to act as an append over there, you can use pointers to slices.

Pointers to slices give you aliasing if you want it but don't provide subslicing--to get everything you ask for, you'd need a different arrangement that I can't think of an example of from in the wild (offset, length, and pointer to struct { capacity int; firstElem *type }).