如何为Go地图分配指针

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