I am having problems assigning a pointer to a map. Maybe this is a bug in Go? Or maybe I am just doing something wrong. The code is here on the playground as well, https://play.golang.org/p/p0NosPtkptz
Here is some super simplified code that illustrates the problem. I am creating an object called collections that has two collection objects in it. I am then looping through those collections and assigning them to a map where the key in the map is the collection ID.
package main
import (
"fmt"
)
type collection struct {
ID string
Name string
}
type collections struct {
Collections []collection
}
type cache struct {
Index int
Collections map[string]*collection
}
func main() {
var c cache
c.Collections = make(map[string]*collection)
// Create 2 Collections
var col1, col2 collection
col1.ID = "aa"
col1.Name = "Text A"
col2.ID = "bb"
col2.Name = "Test B"
// Add to Collections
var cols collections
cols.Collections = append(cols.Collections, col1)
cols.Collections = append(cols.Collections, col2)
fmt.Println("DEBUG Collections Type", cols)
i := 0
for k, v := range cols.Collections {
c.Index = i
c.Collections[v.ID] = &v
fmt.Println("DEBUG k", k)
fmt.Println("DEBUG v", v)
i++
}
fmt.Println("Collection 1", c.Collections["aa"].ID)
fmt.Println("Collection 2", c.Collections["bb"].ID)
fmt.Println(c)
}
The output from this playground code looks like:
DEBUG Collections Type {[{aa Text A} {bb Test B}]}
DEBUG k 0
DEBUG v {aa Text A}
DEBUG k 1
DEBUG v {bb Test B}
Collection 1 bb
Collection 2 bb
{1 map[aa:0x1040a0f0 bb:0x1040a0f0]}
So it seems like the map is for some reason getting the same pointer for each entry. All of the "DEBUG" lines print out what I would expect. However, the three print lines at the very end, do not. Collection 1 should be "aa" not "bb".
Unfortunately, the code in the first answer has an error - a pointer to a local variable coll:
for k, v := range cols.Collections {
coll := v
c.Collections[v.ID] = &coll
Just try to change a property value of a collection:
cols.Collections[0].Name = "Text C"
fmt.Println("Collection 1", cols.Collections[0].Name)
fmt.Println("Collection 1", c.Collections["aa"].Name)
// Collection 1 Text C
// Collection 1 Text A
But the another code will print an expected result:
for k, v := range cols.Collections {
c.Index = i
p := &cols.Collections[k]
c.Collections[v.ID] = p
....
cols.Collections[0].Name = "Text C"
fmt.Println("Collection 1", cols.Collections[0].Name)
fmt.Println("Collection 1", c.Collections["aa"].Name)
// Collection 1 Text C
// Collection 1 Text C
When you are putting &v
into c.Collections[v.ID]
, you are actually assigning same address of loop variable v
.
This address finally holds the last value of your list. That's why you are getting bb Test B
for all key.
Print these value and you will see same address.
fmt.Printf("%p
", c.Collections["aa"])
fmt.Printf("%p
", c.Collections["bb"])
And by copying it to a new variable in the loop scope, the issue is solved. Each step in the loop you will put a new and unique address into the cache.
for k, v := range cols.Collections {
coll := v
c.Collections[v.ID] = &coll
fmt.Println("DEBUG k", k)
fmt.Println("DEBUG v", v)
i++
}
The answer about same pointer is a map contains the address of a variable v, but not array items.
for k, v := range cols.Collections {
......
c.Collections[v.ID] = &v
A right solution is storing a pointer to array, but not a value.
type collections struct {
Collections []*collection
}
cols.Collections = append(cols.Collections, &col1)
cols.Collections = append(cols.Collections, &col2)
Or get address of map item (as the answer higher)
for k, v := range cols.Collections {
c.Index = i
p := &cols.Collections[k]
c.Collections[v.ID] = p