71

Given a struct:

type MyStruct struct {
    A int
    B int
}

and a string with the struct's name

a := "MyStruct"

or

a := "mypkg.MyStruct"

How do I create an instance of my struct from the string name rather than the struct? The idea is that I would create an application with all of the structures linked into the binary but create the runtime instances from the strings. (sort of a meta-meta)

Richard
  • 10,122
  • 10
  • 42
  • 61

3 Answers3

87

There is no central registry of types in Go, so what you ask is impossible in the general case.

You could build up your own registry by hand to support such a feature using a map from strings to reflect.Type values corresponding to each type. For instance:

var typeRegistry = make(map[string]reflect.Type)

func init() {
    myTypes := []interface{}{MyString{}}
    for _, v := range myTypes {
        // typeRegistry["MyString"] = reflect.TypeOf(MyString{})
        typeRegistry[fmt.Sprintf("%T", v)] = reflect.TypeOf(v)
    }
}

You can then create instances of the types like so:

func makeInstance(name string) interface{} {
    v := reflect.New(typeRegistry[name]).Elem()
    // Maybe fill in fields here if necessary
    return v.Interface()
}
James Henstridge
  • 42,244
  • 6
  • 132
  • 114
  • 13
    You can also use `reflect.TypeOf(foo).Name()` instead of typing out each string manually. – Evan Apr 12 '14 at 20:54
  • 2
    I suppose I always knew that it was going to come down do this. I'm not expecting something like this to be added to the language because there are so many good reasons for not supporting this behavior out of the box. – Richard Apr 13 '14 at 18:56
  • 3
    @Evan, it's safer to use `reflect.TypeOf(foo).String()` see small example [here](http://play.golang.org/p/s1agPjsl4n). – Ivan Black Mar 09 '15 at 09:42
  • Remembering that gob can infer and instantiate a type from []byte, but even need to register the struct some way, using gob.Register(MyString{}). Look at gob.Register implementation... – DLopes May 09 '16 at 04:35
22

The Go runtime doesn't exposes a list of types built in the program. And there is a reason: you never have to be able to build all types availables but instead just a subset.

You can build yourself this subset using a map. And you can use the reflect package to create an instance from a reflect.Type.

My solution (see on Go Playground) uses typed nil pointers (instead of empty values) to reduce size of allocations when building the map (compared to @james-henstridge solution).

package main

import (
    "fmt"
    "reflect"
)

var typeRegistry = make(map[string]reflect.Type)

func registerType(typedNil interface{}) {
    t := reflect.TypeOf(typedNil).Elem()
    typeRegistry[t.PkgPath() + "." + t.Name()] = t
}

type MyString string
type myString string


func init() {
    registerType((*MyString)(nil))
    registerType((*myString)(nil))
    // ...
}

func makeInstance(name string) interface{} {
    return reflect.New(typeRegistry[name]).Elem().Interface()
}

func main() {
    for k := range typeRegistry {
        fmt.Println(k)
    }
    fmt.Printf("%T\n", makeInstance("main.MyString"))
    fmt.Printf("%T\n", makeInstance("main.myString"))
}
dolmen
  • 8,126
  • 5
  • 40
  • 42
11

You can create a map of name -> struct "template"

When grabbing a value from a map, you get a copy of the value, the map effectively acts as a factory for your values.

Notice that the values from the map are unique. In order to actually do something with the struct, you'll need to either assert its type or use some reflections based processor (ie: get struct from map, then json decode in to the struct)

Here's a simple Example with one struct in raw form and one pre-filled in. Notice the type assertion on foowv1, that's so I can actually set the value.

package main

import "fmt"

type foo struct {
    a int
}

var factory map[string]interface{} = map[string]interface{}{
    "foo":          foo{},
    "foo.with.val": foo{2},
}

func main() {
    foo1 := factory["foo"]
    foo2 := factory["foo"]
    fmt.Println("foo1", &foo1, foo1)
    fmt.Println("foo2", &foo2, foo2)

    foowv1 := factory["foo.with.val"].(foo)
    foowv1.a = 123
    foowv2 := factory["foo.with.val"]
    fmt.Println("foowv1", &foowv1, foowv1)
    fmt.Println("foowv2", &foowv2, foowv2)
}
David Budworth
  • 11,248
  • 1
  • 36
  • 45
  • 2
    This seems actually really smooth, avoiding reflection all together. – Suau Nov 02 '15 at 13:38
  • 1
    This is not really a factory as it doesn't allow to create new instances. Your code only returns instances defined at comple time. – dolmen Jan 11 '16 at 12:40
  • 2
    it is returning copies of instances defined at compile time. So, they are new in a sense. – David Budworth Jan 12 '16 at 00:13
  • 1
    This creates a circular issue. Extending your example, add another `type bar struct {..}`. How do you type assert unless you know ahead of time which type to use? – utdrmac Nov 05 '17 at 20:56
  • 1
    If your code doesn't know the type it's grabbing ahead of time, then you weren't planning on calling any methods on it, most likely. So just leave it as interface{}. Or, if you have specific interfaces that the concrete types implement, you can always assert to those – David Budworth Nov 06 '17 at 14:46
  • interesting but without reflection you can't to this dynamically. Every time you want to add a new one you need to go and update factory. Would be super nice if go allowed you to do something like call Collect on an interface to have it return all structs that implement it. – michael.schuett Dec 10 '17 at 21:21