英文:
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 caseappend
simply returnsx[:len(x)+1]
(ie, extends the length of the returned slice to contain one extra element)cap(x) == len(x)
, in which caseappend
creates another array by copying over the contents ofx
and then appendsy
as thelen(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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论