【发布时间】:2022-12-18 03:54:15
【问题描述】:
在 Go 的slice tricks wiki 和 Go 库(例如,this example)中,您有时会看到类似下面的代码将切片复制到新的后备数组中。
// 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...)
这是我认为我理解的:
- 作为
append的第二个参数的切片中的所有项目都被复制到新的支持数组。 - 在
append的第一个参数中,代码使用a full slice expression。 (我们可以将第一个参数重写为a[0:0:0],但如果省略第一个参数,则假定为第一个0。我认为这与此处更大的含义无关。) - 基于the spec,生成的切片应与原始切片具有相同的类型,并且长度和容量应为零。
- (同样,没有直接关系,但我知道您可以使用
copy而不是append,这样阅读起来更清晰。)
然而,我还是不能完全理解为什么语法 append(someSlice[:0:0], someSlice...) 创建一个新的后备数组。我最初也很困惑,为什么 append 操作没有弄乱(或截断)原始切片。
现在我的猜测:
- 我假设所有这些都是必要且有用的,因为如果您只分配
newSlice := oldSlice,那么对其中一个的更改将反映在另一个中。通常,你不会想要那样。 - 因为我们没有将
append的结果分配给原始切片(在 Go 中是正常的),所以原始切片没有任何变化。它不会以任何方式被截断或更改。 - 因为
anySlice[:0:0]的长度和容量都是零,如果要将anySlice的元素分配给结果,Go 必须创建一个新的后备数组。这是为什么创建了一个新的后备阵列? - 如果
anySlice...没有元素会怎样? snippet on the Go Playground 建议如果您在空切片上使用此附加技巧,则副本和原始切片最初具有相同的后备数组。 (编辑: 作为a commenter explains,我误解了这个 sn-p。 sn-p 表明这两个项目最初是相同的,但是都还没有后备阵列.它们最初都指向一个通用的零值。)由于这两个切片的长度和容量都为零,因此当您向其中一个添加任何内容时,那个得到一个新的后备阵列。所以,我猜,效果还是一样的。即append复制后,两个切片不会相互影响。 -
other playground snippet 建议如果一个切片的元素多于零,
appendcopy 方法会立即导致一个新的后备数组。在这种情况下,可以说,两个结果切片立即分开。
我可能对此太担心了,但我希望得到更全面的解释为什么append(a[:0:0], a...) 技巧就是这样运作的。
【问题讨论】:
-
您在第一部分解释了操作,在第二部分又解释了操作——我不确定您对什么感到困惑,因为您似乎已经回答了自己的问题。如果您正在寻找确认,是的,追加会创建一个新数组,因为您显然不能扩展容量为 0 的切片。
-
pkg.go.dev/builtin@go1.19.3#append“如果它有足够的容量,目标将被重新切片以容纳新元素。如果没有,将分配一个新的底层数组。”. cap=
0对于非空切片是不够的,分配一个新数组是必要的。 cap=0空切片就足够了,不需要分配新的数组。 -
您的游乐场示例也没有显示您的想法,空切片没有指向同一个支持数组,它们指向编译器使用的零值,这是一个您不会看到的实现细节挖掘不安全。这也与您将看到的获取空结构地址的值相同。
-
@mkopriva 虽然很傻,但如果您想将其发布为答案,我可能会接受。我想只是说规范证实了我的怀疑。
-
@JimB 再次感谢:我将更新帖子以反映您关于空切片的观点。