【问题标题】:Why does `append(x[:0:0], x...)` copy a slice into a new backing array in Go?为什么 `append(x[:0:0], x...)` 将切片复制到 Go 中的新后备数组中?
【发布时间】: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 建议如果一个切片的元素多于零,append copy 方法会立即导致一个新的后备数组。在这种情况下,可以说,两个结果切片立即分开。

我可能对此太担心了,但我希望得到更全面的解释为什么append(a[:0:0], a...) 技巧就是这样运作的。

【问题讨论】:

  • 您在第一部分解释了操作,在第二部分又解释了操作——我不确定您对什么感到困惑,因为您似乎已经回答了自己的问题。如果您正在寻找确认,是的,追加会创建一个新数组,因为您显然不能扩展容量为 0 的切片。
  • pkg.go.dev/builtin@go1.19.3#append“如果它有足够的容量,目标将被重新切片以容纳新元素。如果没有,将分配一个新的底层数组。”. cap=0 对于非空切片是不够的,分配一个新数组是必要的。 cap=0 空切片就足够了,不需要分配新的数组。
  • 您的游乐场示例也没有显示您的想法,空切片没有指向同一个支持数组,它们指向编译器使用的零值,这是一个您不会看到的实现细节挖掘不安全。这也与您将看到的获取空结构地址的值相同。
  • @mkopriva 虽然很傻,但如果您想将其发布为答案,我可能会接受。我想只是说规范证实了我的怀疑。
  • @JimB 再次感谢:我将更新帖子以反映您关于空切片的观点。

标签: arrays go slice


【解决方案1】:

因为 anySlice[:0:0] 的长度和容量都是零,如果要将 anySlice 的元素分配给结果,Go 必须创建一个新的支持数组。这就是创建新后备阵列的原因吗?

因为容量是0,是的。


https://pkg.go.dev/builtin@go1.19.3#append

如果它有足够的容量,则重新切片目的地以容纳新元素。如果没有,将分配一个新的底层数组。

  • cap=0 对于非空切片是不够的,必须分配一个新数组。

  • cap=0 对空切片足够了,不需要分配新的数组。

【讨论】:

    猜你喜欢
    • 2021-11-18
    • 2014-02-11
    • 1970-01-01
    • 1970-01-01
    • 2014-11-06
    • 2011-05-04
    • 2019-09-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多