append()方法在幕后是如何工作的,导致了这个问题?

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

How does append() work behind the scenes that's causing this?

问题

我对Go语言还不太熟悉,但我会尽力帮你翻译。以下是你要翻译的内容:

我对Go语言还不太熟悉,但我在追踪我的代码库中的一个错误。在这个过程中,我将问题简化为对append()的单个调用,但是我无法弄清楚它为什么会表现出这种行为...

func main() {
	foo := []string{"a", "b", "c"}
	fmt.Printf("before: %v\n", foo)

	i := 0
	noop(append(foo[:i], foo[i+1:]...)) // -- 调用append,但不对结果进行任何操作
	fmt.Printf(" after: %v\n", foo)
}

func noop(a interface{}) {} // -- 避免出现"evaluated but not used"错误

在这里尝试一下

那么,到底发生了什么?

英文:

I'm pretty new to Go and trying to track down a bug in my codebase. In the process I've distilled the problem down to a single call to append(), but can't figure out why it's behaving the way it is...

func main() {
	foo := []string{"a", "b", "c"}
	fmt.Printf("before: %v\n", foo)

	i := 0
	noop(append(foo[:i], foo[i+1:]...)) // -- call append, but do nothing with the result
	fmt.Printf(" after: %v\n", foo)
}

func noop(a interface{}) {} // -- avoid "evaluated but not used" errors

Try it here

So, what the heck is really going on here?

答案1

得分: 6

append(foo[:i], foo[i+1:]...) 的作用如下:

  1. 它获取 foo[:i] 切片,即 foo[:0],它是一个长度为 0、容量至少为 3 的切片。

  2. 一旦容量足够以追加值,底层数组就会被重用。

  3. 你将 bc 分别写入底层数组的索引 01

然后,你检查使用我们刚刚修改的底层数组的 foo 变量,它包含了 b c c 这些值。

与下面的代码进行比较:

noop(append(foo[:i], "a", "a", "a", "a", "a"))

这里要追加的值的列表比当前容量长。因此,运行时会分配一个新的底层数组。而且你不会改变 foo。https://play.golang.org/p/RooYG_p9Z8

英文:

append(foo[:i], foo[i+1:]...) does the following:

  1. It takes the foo[:i] slice, which is foo[:0] and basically a slice with length 0 and capacity (at least) 3.

  2. As soon as the capacity is enough to append the values - the underlying array is reused

  3. You write b and c into the indexes 0 and 1 correspondingly of the underlying array.

Then you check the foo variable that uses the underlying array we just modified and that contains the b c c values.

Compare with the following:

noop(append(foo[:i], "a", "a", "a", "a", "a"))

Here the list of values to append is longer than the current capacity. So the runtime allocates a new underlying array. And you don't mutate the foo. https://play.golang.org/p/RooYG_p9Z8

答案2

得分: 3

如果s的容量不足以容纳额外的值,append会分配一个足够大的新底层数组,该数组可以容纳现有的切片元素和额外的值。否则,append会重用底层数组。

具体来说,这里的sfoo[:0],它的容量至少为3,因为foo的长度为3。s没有元素,而append想要添加两个元素[b c],所以有空间,append将重用foo[:0]的底层数组,该数组与foo的底层数组相同。

因此,它将项目[b c]放在数组的开头,并返回一个长度为2的新切片。

但是你仍然在看foo,它是同一个数组的切片,但长度仍为3;第三个元素是未被修改的c,所以foo现在是[b c c]

英文:

> If the capacity of s is not large enough to fit the additional values, append allocates a new, sufficiently large underlying array that fits both the existing slice elements and the additional values. Otherwise, append re-uses the underlying array.

Breaking it down, s here is foo[:0], which has a capacity of at least 3, since the length of foo is 3. s has 0 elements, and append wants to add two elements, [b c], so there is room, and append will re-use the underlying array of foo[:0] which is the same as the underlying array of foo.

So it places the items [b c] at the beginning of the array, and returns a new slice of that array with a length of 2.

But you're still looking at foo, which is a slice of that same array, but still has a length of 3; the third item is the c which hasn't been touched, so foo is now [b c c].

答案3

得分: 2

我对Go语言也比较新,但我对append的理解是它将所有后续的参数追加到第一个参数中。

所以基本上你所做的是将foo[1]foo[2]追加到foo[0]foo[1],而不实际改变foo的长度。

如果你想保留原始切片,你需要的是这个

英文:

I'm also relatively new to go, but the way I understand append is that it appends all the following arguments to the first argument.

So essentially what you are doing is appending foo[1] and foo[2] to foo[0] and foo[1] without actually changing the length of foo.

If you want to preserve the original slice what you want is this

huangapple
  • 本文由 发表于 2017年6月9日 07:55:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/44447271.html
匿名

发表评论

匿名网友

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

确定