英文:
Why does `append(x[:0:0], x...)` copy a slice into a new backing array in Go?
问题
在Go的slice tricks wiki和Go库(例如,这个示例)中,有时会看到以下类似的代码将一个切片复制到一个新的后备数组中。
// 在库中,可能是函数的最后...
return append(whateverSlice[:0:0], whateverSlice...)
// 在赋值语句中,就像在wiki示例中一样...
b = append(a[:0:0], a...)
以下是我理解的内容:
append
函数的第二个参数是要复制到新后备数组中的切片的所有项。append
函数的第一个参数使用了完整的切片表达式(我们可以将第一个参数重写为a[0:0:0]
,但如果省略第一个0
,它将被提供。我认为这与更大的意义无关。)- 根据规范,结果切片应该与原始切片具有相同的类型,并且长度和容量都为零。
- (再次强调,这与直接相关,但我知道你可以使用
copy
而不是append
,并且更容易阅读。)
然而,我仍然无法完全理解为什么语法append(someSlice[:0:0], someSlice...)
会创建一个新的后备数组。最初我也对append
操作为什么不会影响(或截断)原始切片感到困惑。
现在是我的猜测:
- 我猜想所有这些操作都是必要且有用的,因为如果你只是赋值
newSlice := oldSlice
,那么对其中一个的更改将反映在另一个上。通常情况下,你不会希望出现这种情况。 - 因为我们没有将
append
的结果赋值给原始切片(这在Go中是正常的),所以原始切片不会发生任何变化。它不会被截断或以任何方式改变。 - 因为
anySlice[:0:0]
的长度和容量都为零,如果Go要将anySlice
的元素分配给结果,它必须创建一个新的后备数组。这是为什么会创建一个新的后备数组的原因吗? - 如果
anySlice...
没有元素会发生什么?一个Go Playground上的片段表明,如果你在空切片上使用这个append
技巧,复制和原始初始时具有相同的后备数组。(编辑:正如一位评论者解释的,我误解了这个片段。该片段显示这两个项最初都是相同的,但它们都没有后备数组。它们最初都指向一个通用的零值。)由于这两个切片的长度和容量都为零,一旦你向其中一个添加任何内容,那个切片就会获得一个新的后备数组。因此,我猜想,效果仍然是相同的。也就是说,在append
进行复制后,这两个切片不能相互影响。 - 这另一个Playground片段表明,如果一个切片有多个元素,
append
复制方法会立即导致一个新的后备数组。在这种情况下,两个结果切片立即分离。
我可能对此过于担心,但我希望能更全面地解释为什么append(a[:0:0], a...)
这个技巧的工作方式。
英文:
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 asa[0:0:0]
, but the first0
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 ofappend
, 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 ofanySlice
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 byappend
. - 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.
答案1
得分: 2
因为anySlice[:0:0]
的长度和容量都为零,如果要将anySlice
的元素分配给结果,Go必须创建一个新的支持数组。这就是为什么会创建一个新的支持数组的原因。
因为容量为0
,是的。
https://pkg.go.dev/builtin@go1.19.3#append
如果目标切片具有足够的容量,它将被重新切片以容纳新的元素。如果没有足够的容量,将会分配一个新的底层数组。
- 对于非空切片来说,
cap=0
是不足够的,需要分配一个新的数组。
英文:
> 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/builtin@go1.19.3#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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论