为什么Go语言中的切片 `append()` 函数不接受引用?

huangapple go评论155阅读模式
英文:

Why does Go slice `append()` not take a reference?

问题

Go语言的切片append()函数可能会分配一个新的后备数组来为新的元素腾出空间。因此,在调用z = append(x, y)之后,如果修改了z的前面的元素,x实际上是未指定的——它可能具有与z相同的后备数组,也可能没有,而z可以使用z[0] = foo来改变其后备数组,这可能会修改x的后备数组。

那么,为什么要让这种丑陋的情况浮出水面呢?与其将append的结果分配给除了第一个参数之外的任何变量都视为程序错误,为什么不让append接受一个*[]T参数,这样就不需要重新赋值,也不会留下未定义的变量。

这并不能解决所有情况,因为a = x; append(&x, y)仍然会使a变为未定义,但是部分改进总比没有好。

英文:

Go Lang's slice append() might allocate a new backing array to make room for the new item. Thus, after a call z = append(x,y), if z's front most elements are modified, x is essentially unspecified -- it might or might not have the same backing array as z, and z could mutate its backing array with z[0] = foo, which thus might or might not modify x's backing array.

So, why let this ugliness surface? Instead making it a program bug to assign the result of append to anything but its first argument, why not have append take a *[]T instead, so no reassignment is needed and no undefined variable is left dangling.

This wouldn't solve every case, because a = x; append(&x,y) would still make a undefined, but a partial improvement seems better than none.

答案1

得分: 3

在你的第一个例子中(z = append(x, y)),并不完全正确说x是未定义的。相反,x仍然指向x的原始内容,而z指向这些内容以及其他内容。正如你提到的,有两种可能性:

  • cap(x) > len(x),在这种情况下,append只是返回x[:len(x)+1](即,扩展返回的切片的长度以包含一个额外的元素)
  • cap(x) == len(x),在这种情况下,append通过复制x的内容创建另一个数组,然后将y附加为第len(x)个元素。

在这两种情况下,x在很大程度上保持不变,即x的所有元素仍然存在。显然,现在你必须小心,因为你可能有两个引用指向相同的底层数据,但是保留x可能是有用的。

话虽如此,我同意也许让append接受一个指针可能会更简单,而且也许我刚刚描述的这种可能的用例并不常见,不值得采用可能会令人困惑、稍微冗长的语义。

英文:

It's not quite true that in your first example (z = append(x, y)), x is undefined. Instead, x still points to the original contents of x, while z points to those contents and then some. As you mention, there are two possibilities:

  • cap(x) > len(x), in which case append simply returns x[:len(x)+1] (ie, extends the length of the returned slice to contain one extra element)
  • cap(x) == len(x), in which case append creates another array by copying over the contents of x and then appends y as the len(x)-th element.

In either of these cases, x is left largely unchanged in the sense that all of the elements of x are still there. You obviously have to be careful now that you potentially have two references to the same underlying data, but the point stands that keeping x around can be useful.

That said, I agree that maybe having append take a pointer might be simpler, and maybe this possible use case that I've just described isn't common enough to warrant the potentially confusing, mildly more verbose semantics.

huangapple
  • 本文由 发表于 2014年1月10日 10:40:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/21035279.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定