Home > Software design >  Why does `append(x[:0:0], x...)` copy a slice into a new backing array in Go?
Why does `append(x[:0:0], x...)` copy a slice into a new backing array in Go?

Time:12-08

On Go's slice tricks wiki and Go libraries (e.g., this example), you sometimes see code like the following to copy a slice into a new backing array.

// In a library at the end of a function perhaps...
return append(whateverSlice[:0:0], whateverSlice...)

// In an assignment, as in the wiki example...
b = append(a[:0:0], a...)

Here's what I think I understand:

  • All of the items in the slice that is the second parameter to append are copied over to a new backing array.
  • In the first parameter to append, the code uses a full slice expression. (We can rewrite the first parameter as a[0:0:0], but the first 0 will be supplied if omitted. I assume that's not relevant to the larger meaning here.)
  • Based on the spec, the resulting slice should have the same type as the original, and it should have a length and capacity of zero.
  • (Again, not directly relevant, but I know that you can use copy instead of append, and it's a lot clearer to read.)

However, I still can't fully understand why the syntax append(someSlice[:0:0], someSlice...) creates a new backing array. I was also initially confused why the append operation didn't mess with (or truncate) the original slice.

Now for my guesses:

  • I'm assuming that all of this is necessary and useful because if you just assign newSlice := oldSlice, then changes to the one will be reflected in the other. Often, you won't want that.
  • Because we don't assign the result of the append to the original slice (as is normal in Go), nothing happens to the original slice. It isn't truncated or changed in any way.
  • Because the length and capacity of anySlice[:0:0] are both zero, Go must create a new backing array if it's going to assign the elements of anySlice to the result. Is this why a new backing array is created?
  • What would happen if anySlice... had no elements? A snippet on the Go Playground suggests that if you use this append trick on an empty slice, the copy and the original initially have the same backing array. (Edit: as a commenter explains, I misunderstood this snippet. The snippet shows that the two items are initially the same, but neither has a backing array yet. They both point initially to a generic zero value.) Since the two slices both have a length and capacity of zero, the minute you add anything to one of them, that one gets a new backing array. Therefore, I guess, the effect is still the same. Namely, the two slices cannot affect each other after the copy is made by append.
  • This other playground snippet suggests that if a slice has more than zero elements, the append copy method leads immediately to a new backing array. In this case, the two resulting slices come apart, so to speak, immediately.

I am probably worrying way too much about this, but I'd love a fuller explanation of why the append(a[:0:0], a...) trick works the way it does.

CodePudding user response:

Because the length and capacity of anySlice[:0:0] are both zero, Go must create a new backing array if it's going to assign the elements of anySlice to the result. Is this why a new backing array is created?

Because capacity is 0, yes.


https://pkg.go.dev/[email protected]#append

If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated.

  • cap=0 is NOT sufficient for non-empty slice, allocating a new array is necessary.
  • Related