反思:获取正确的接口结构类型

Consider this:

type myStruct struct {
    Foo string `json:"foo"`
}

func main() {
    somelibrary.DoThing(func(thing myStruct) {
        // myStruct should contain unmarshaled JSON
        // provided by somelibrary

        fmt.Printf("%v
", thing)
    })
}

I'm new to Go, so I fear this might not be idiomatic code. I'd like to implement somelibrary.DoThing so it correctly infers the struct type from the function argument via reflection, if it's possible. Here's what I have:

const jsonData := []byte{`{"foo": "bar"}`}

func DoThing(fn interface{}) {
    // Get first arg of the function
    firstArg := reflect.TypeOf(fn).In(0)
    structPtr := reflect.New(firstArg)

    // Convert to Interface
    // Note that I can't assert this to .(myStruct) type
    instance := structPtr.Elem().Interface()

    // Unmarshal the JSON
    json.Unmarshal(jsonData, &instance)

    // Call the function
    vfn := reflect.ValueOf(fn)
    vfn.Call([]reflect.Value{reflect.ValueOf(instance)})
}

Without knowing the struct type beforehand, json.Unmarshal just assumes that instance is map[string]interface{}, so I get a panic when calling vfn.Call(...):

panic: reflect: Call using map[string]interface {} as type main.myStruct

Is it possible to convert the instance interface into the correct type? In other words, can I do type type assertion by passing a string (or using some reflection method) instead of having the type available to the program as a symbol?

Yes, this is possible. Here is your code updated:

func DoThing(fn interface{}) {
    // Get first arg of the function
    firstArg := reflect.TypeOf(fn).In(0)

    // Get the PtrTo to the first function parameter
    structPtr := reflect.New(firstArg)

    // Convert to Interface
    // Note that I can't assert this to .(myStruct) type
    instance := structPtr.Interface()

    // Unmarshal the JSON
    json.Unmarshal(jsonData, instance)

    // Call the function
    vfn := reflect.ValueOf(fn)
    vfn.Call([]reflect.Value{structPtr.Elem()})
}

Changes made:

  1. Pass structPtr (a pointer) to json.Unmarshal; pass a value and you will not see the changes
  2. Remove taking the address of instance when passing to json.Unmarshal; there is usually never a good reason to have a pointer to an interface
  3. Use structPtr instead of instance when calling fn

https://play.golang.org/p/POmOyQBJYC