Max Heiber
Max Heiber

Reputation: 15492

Creating a map from an array of struct pointers

I have an array of pointers to structs. The structs have a name field. I want to create a map from names to pointers to structs.

Why are all of the values in the registry map identical?

package main

import "fmt"

type Thing struct {
    Name  string
    Value int
}

type Registry map[string]*Thing

func toRegistry(things *[]Thing) Registry {

    registry := make(Registry)

    for _, thing := range *things {
        registry[thing.Name] = &thing
    }
    return registry
}

func main() {

    things := []Thing{{"thingA", 1}, {"thingB", 2}}
    registry := toRegistry(&things)
    fmt.Println(registry)

}

Sample output: map[thingB:0x10436180 thingA:0x10436180]

GO

EDIT

Per @tvblah's suggestion, things was already a slice, so there's no need to point to it:

package main

import "fmt"

type Thing struct {
    Name  string
    Value int
}

type Registry map[string]*Thing

func toRegistry(things []Thing) Registry {

    registry := make(Registry)

    for _, thing := range things {
        registry[thing.Name] = &thing
    }
    return registry
}

func main() {

    things := []Thing{{"thingA", 1}, {"thingB", 2}}
    registry := toRegistry(things)
    fmt.Println(registry)

GO

Upvotes: 1

Views: 1101

Answers (2)

Sundrique
Sundrique

Reputation: 637

You may reassign thing to another local variable on each iteration and store new variable in registry.

package main

import "fmt"

type Thing struct {
    Name  string
    Value int
}

type Registry map[string]*Thing

func toRegistry(things *[]Thing) Registry {

    registry := make(Registry)

    for _, thing := range *things {
        t := thing
        registry[thing.Name] = &t
    }
    return registry
}

func main() {

    things := []Thing{{"thingA", 1}, {"thingB", 2}}
    registry := toRegistry(&things)
    fmt.Println(registry)

}

Playground

Upvotes: 2

user4752457
user4752457

Reputation: 206

Each map value is a pointer to the single local variable thing.

One fix is to add an pointer to the slice element:

func toRegistry(things []Thing) Registry {

  registry := make(Registry)

  for i := range things {
    registry[things[i].Name] = &things[i]
  }
  return registry
}

playground

Another option is to store pointers to Thing in the slice:

func toRegistry(things []*Thing) Registry {
  registry := make(Registry)
  for _, thing := range things {
    registry[thing.Name] = thing
  }
  return registry
}

func main() {
  things := []*Thing{&Thing{"thingA", 1}, &Thing{"thingB", 2}}
  registry := toRegistry(things)
  fmt.Println(registry)
}

playground

I changed the function argument from a pointer to a slice to a slice. This change has no impact on the issue raised on the question, but it's generally how Go code is written. Pointers to slices are rarely used in Go.

Upvotes: 3

Related Questions