After a golang range copy all items point to the last item of the src slice

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

After a golang range copy all items point to the last item of the src slice

问题

在将一个切片(src)中的所有项复制到一个新切片(dst)后,dst 中的所有项都指向 src 的最后一项。

package main

import (
	"fmt"
)

func main() {
	src := []string{"a", "b", "c"}
	dst := []*string{}
	for _, val := range src {
		dst = append(dst, &val)
	}

	for i, s := range dst {
		fmt.Printf("%v - %v\n", i, *s)
	}
}

输出结果为:

0 - c
1 - c
2 - c

为什么 "a" 和 "b" 没有被复制到 dst 切片中?

英文:

After I copy all items from a slice (src) into a new slice (dst) all items in dst point to the last item of src.

package main

import (
	"fmt"
)

func main() {
	src := []string{"a", "b", "c"}
	dst := []*string{}
	for _, val := range src {
		dst = append(dst, &val)
	}

	for i, s := range dst {
		fmt.Printf("%v - %v\n", i, *s)
	}
}

>>> 0 - c
>>> 1 - c
>>> 2 - c

Why aren't "a" and "b" copied into the dst slice?

答案1

得分: 7

解决方案很简单,但在range语句中的预期方面与直觉相反。

变量val只被实例化一次,并在每次迭代中保存src[i]。它并不具有指向src[i]的实际指针。相反,src[i]的值被复制到分配给val的内存中。通过获取val的指针,你得到的正是这个值,而不是指向src[i]的指针。

package main

import (
	"fmt"
)

func main() {
	src := []string{"a", "b", "c"}
	dst := []*string{}
	for _, val := range src {
		dst = append(dst, &val)
	}

	for i, s := range dst {
		fmt.Printf("%v - %v - %p\n", i, *s, s)
	}

    //修改第一个索引的内容
	*dst[0] = "hi val"

	for i, s := range dst {
		fmt.Printf("%v - %v - %p\n", i, *s, s)
	}
}

输出结果为:

0 - c - 0x1040a120
1 - c - 0x1040a120
2 - c - 0x1040a120
0 - hi val - 0x1040a120
1 - hi val - 0x1040a120
2 - hi val - 0x1040a120

事后看来,这是显而易见的——for _, val := range src——> :=——> range src,但如果你不知道这一点,它可能会让你措手不及,因此需要提醒一下。

另外,这也适用于src作为一个映射的情况。

英文:

The solution is simple, but counter intuitive in terms of what you would expect of the range statement.

The variable val is instantiated only once and holds the value of src[i] in every iteration. It does not has the actual pointer to src[i]. Instead the value of src[i] is copied to the memory that is assigned to val. By taking the pointer of val you get exactly that, and not the pointer to src[i].

package main

import (
	"fmt"
)

func main() {
	src := []string{"a", "b", "c"}
	dst := []*string{}
	for _, val := range src {
		dst = append(dst, &val)
	}

	for i, s := range dst {
		fmt.Printf("%v - %v - %p\n", i, *s, s)
	}

    //modify only contents of first index
	*dst[0] = "hi val"

	for i, s := range dst {
		fmt.Printf("%v - %v - %p\n", i, *s, s)
	}
}

>>>>
0 - c - 0x1040a120
1 - c - 0x1040a120
2 - c - 0x1040a120
0 - hi val - 0x1040a120
1 - hi val - 0x1040a120
2 - hi val - 0x1040a120

While in hindsight this is obvious - for _, val -> := <- range src - it can can bite you in the ass if you are unaware of it, so therefore this warning.

PS. this also applies to src as a map.

答案2

得分: 5

根据@RickyA的回答,为了实现你想要的效果,你需要进行以下操作:

for i := range src {
    dst[i] = &src[i]
}
英文:

Adding to @RickyA's answer.

To achieve what you were trying to do you need to do:

for i := range src {
    dst[i] = &amp;src[i]
}

huangapple
  • 本文由 发表于 2016年4月28日 21:24:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/36916088.html
匿名

发表评论

匿名网友

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

确定