I'm pretty new to Go and trying to track down a bug in my codebase. In the process I've distilled the problem down to a single call to append()
, but can't figure out why it's behaving the way it is...
func main() {
foo := []string{"a", "b", "c"}
fmt.Printf("before: %v
", foo)
i := 0
noop(append(foo[:i], foo[i+1:]...)) // -- call append, but do nothing with the result
fmt.Printf(" after: %v
", foo)
}
func noop(a interface{}) {} // -- avoid "evaluated but not used" errors
So, what the heck is really going on here?
append(foo[:i], foo[i+1:]...)
does the following:
It takes the foo[:i]
slice, which is foo[:0]
and basically a slice with length 0
and capacity (at least) 3
.
As soon as the capacity is enough to append the values - the underlying array is reused
You write b
and c
into the indexes 0
and 1
correspondingly of the underlying array.
Then you check the foo
variable that uses the underlying array we just modified and that contains the b c c
values.
Compare with the following:
noop(append(foo[:i], "a", "a", "a", "a", "a"))
Here the list of values to append is longer than the current capacity. So the runtime allocates a new underlying array. And you don't mutate the foo
. https://play.golang.org/p/RooYG_p9Z8
If the capacity of
s
is not large enough to fit the additional values,append
allocates a new, sufficiently large underlying array that fits both the existing slice elements and the additional values. Otherwise,append
re-uses the underlying array.
Breaking it down, s
here is foo[:0]
, which has a capacity of at least 3, since the length of foo
is 3. s
has 0 elements, and append wants to add two elements, [b c]
, so there is room, and append
will re-use the underlying array of foo[:0]
which is the same as the underlying array of foo
.
So it places the items [b c]
at the beginning of the array, and returns a new slice of that array with a length of 2.
But you're still looking at foo
, which is a slice of that same array, but still has a length of 3; the third item is the c
which hasn't been touched, so foo
is now [b c c]
.
I'm also relatively new to go, but the way I understand append
is that it appends all the following arguments to the first argument.
So essentially what you are doing is appending foo[1]
and foo[2]
to foo[0]
and foo[1]
without actually changing the length of foo
.
If you want to preserve the original slice what you want is this