Reputation: 16029
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?
Upvotes: 3
Views: 841
Reputation: 16029
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.
Upvotes: 7
Reputation: 99331
Adding to @RickyA's answer.
To achieve what you were trying to do you need to do:
for i := range src {
dst[i] = &src[i]
}
Upvotes: 5