How to make a new type struct indirectly or call an interface function indirectly?

I was recently hacking on some Golang code and came up with this switch / case statement:

        switch what {
        case "Type1"  : names[name] = new(Type1)
        case "Type2" : names[name] = new(Type2)
        case "Type3": names[name] = new(Type3)
        }

Depending upon what`s value, I want to create a new type struct instance. How would this be possible without a switch / case statement, or equivalent if / else statement? I was thinking that what could be the key to a hash table and the type of type could be the value. But what is the Golang syntax for new(indirect type) ?

Similarly, let’s say that we want to call one of n interface functions on one of those new type structs, this can be also done with a switch / case statement as follows:

type TypeStar interface {
    Func1()
    Func2()
}

type Type1 struct {}

func (a *Type1) Func() { fmt.Printf ("func1,type1") }
func (a *Type1) Func2() { fmt.Printf ("func2,type1") }

type Type2 struct {}

func (a *Type2) Func1() { fmt.Printf ("func1,type2") }
func (a *Type2) Func2() { fmt.Printf ("func2,type2") }

type Type3 struct {}

func (a *Type3) Func1() { fmt.Printf ("func1,type3") }
func (a *Type3) Func2() { fmt.Printf ("func2,type3") }
...
        if object, ok := names[name]; ok {
                switch what {
                case "func1"  : object.func1()
                case "func2" : object.func2()
                }
        }

But how change this code to eliminate the switch / case statement and use e.g. a hash table instead, e.g. what is the Golang syntax to store the indirect interface function as a hash table value so that the interface function can be looked up in the hash table and then called?

The way you’re doing it (or with a map, or if … else, etc.) is the only way you can go from a string to a type. You can use the reflect package to “parameterize” the type, but you must first have a reference to the type. i.e., given this code:

type MyType struct{}

func main() {
    x := newOf("MyType")
}

The only way for newOf to work is if you have some registry that maps names to types. This is the way, for example, the database/sql driver packages work: The reason for the _ imports for driver packages is required is to ensure they’re registered in some global database/sql driver registry, like with an init function.

1 Like

Thanks, @skillian! That makes a lot of sense.

Sorry, I expanded my original question while you were replying. It would be great if you could offer more wisdom regarding the second part of the question too. Thanks!

For the second part, I think you want the reflect package. You can do stuff like this (error handling omitted for brevity):

v := reflect.ValueOf(obj1)
f := v.MethodByName("Func1")
res := f.Call(nil)
1 Like

Thanks @skillian!

All your suggestions worked very well.

With the first suggestion then I was confused at the start, thinking that “newOf()” was a regular Golang built-in function. But then I assumed you meant to pre-create the wanted types in a kind of registry and then ‘copy’ the wanted type from the registry. This seemed to work well. I hope I understood you correctly :slight_smile:

With the second suggestion then it all just worked. Although I’m wondering if the switch / case statement would end up faster than the .MethodByName() function? I guess I can profile it to find out with differing numbers of switch cases?

Thanks again for your help!

Reflection will be slower than the switch/cases. Sorry about the ambiguity with newOf, but I think you got it now. To be clear, this is something like what I imagine doing:

var typeMap = map[string]func() interface{} {
    "type1": func() interface{} { return new(Type1) },
    "type2": func() interface{} { return new(Type2) },
    "type3": func() interface{} { return new(Type3) },
}

func newOf(name string) interface{} {
    return typeMap[name]()
}
1 Like

Thanks @skillian for the clarification and the code.

I did it slightly differently and am wondering if my different way is flawed somehow:

typeRegistry := map[string]TypeStar {
    "Type1" : new(Type1) ,
    "Type2" : new(Type2) }
names[name] = typeRegistry[what]

It seems to work but has the disadvantage of pre-instantiating the types (slightly worse for memory?)? And then copying them could in theory be slower than creating them on demand? In short I like your clarified code with the functions better than my code :slight_smile: