Why does a
remain the same? Does append()
generate a new slice?
package main
import (
"fmt"
)
var a = make([]int, 7, 8)
func Test(slice []int) {
slice = append(slice, 100)
fmt.Println(slice)
}
func main() {
for i := 0; i < 7; i++ {
a[i] = i
}
Test(a)
fmt.Println(a)
}
Try this, which I think makes it clear. the underlying array is changed but our slice is not,
print
just printslen()
chars, by another slice to thecap()
, you can see the changed array:NOTICE that append generates a new slice if cap is not sufficient. @kostix's answer is correct, or you can pass slice argument by pointer!
In order to make your code work without having to return the slice from Test, you can pass a pointer like this:
Ref: http://criticalindirection.com/2016/02/17/slice-with-a-pinch-of-salt/
Output of the example from the link explains the behavior of slices in Go.
Creating slice a.
Slice b refers to the 2, 3, 4 indices in slice a. Hence, the capacity is 5 (= 7-2).
Modifying slice b, also modifies a, since they are pointing to the same underlying array.
Appending 1 to slice b. Overwrites a.
Appending 2 to slice b. Overwrites a.
Appending 3 to slice b. Here, a new copy is made as the capacity is overloaded.
Verifying slices a and b point to different underlying arrays after the capacity-overload in the previous step.
I think the original answer is not exactly correct.
append()
changed both the slices and the underlying array even though the underlying array is changed but still shared by both of the slices.As specified by the Go Doc:
Slices are just wrapper values around arrays, meaning that they contain information about how they slice an underlying array which they use to store a set of data. Therefore, by default, a slice, when passed to another method, is actually passed by value, instead of reference/pointer even though they will still be using the same underlying array. Normally, arrays are also passed by value too, so I assume a slice points at an underlying array instead of store it as a value. Regarding your question, when you run passed your slice to the following function:
you actually passed a copy of your slice along with a pointer to the same underlying array.That means, the changes you did to the
slice
didn't affect the one in themain
function. It is the slice itself which stores the information regarding how much of an array it slices and exposes to the public. Therefore, when you ranappend(slice, 1000)
, while expanding the underlying array, you also changed slicing information ofslice
too, which was kept private in yourTest()
function.However, if you have changed your code as follows, it might have worked:
The reason is that you expanded
a
by sayinga[:cap(a)]
over its changed underlying array, changed byTest()
function. As specified here:In your example the
slice
argument of theTest
function receives a copy of the variablea
in the caller's scope.Since a slice variable holds a "slice descriptor" which merely references an underlying array, in your
Test
function you modify the slice descriptor held in theslice
variable several times in a row, but this does not affect the caller and itsa
variable.Inside the
Test
function, the firstappend
reallocates the backing array under theslice
variable, copies its original contents over, appends100
to it, and that's what you're observing. Upon exiting fromTest
, theslice
variable goes out of scope and so does the (new) underlying array that slice references.If you want to make
Test
behave likeappend
, you have to return the new slice from it — just likeappend
does — and require the callers ofTest
to use it in the same way they would useappend
:Please read this article thoroughly as it basically shows you how to implement
append
by hand, after explaining how slices are working internally. Then read this.