lundi 14 janvier 2019

Go Reflect field name to specific interface

I have a struct type with a number of fields that all implement a Renderer interface. The field types implement the interface with pointer receivers. I would like to have a function that takes the field name and calls the Render method on that field. I am able to locate the field and get lots of information about it, but doing the type assertion seems to be biting me because of the pointer receivers. Here's some code that shows my problem:

package main

import (
    "fmt"
    "reflect"
)

type Renderer interface {
    Render()
}

type First struct {
    ExampleField Field
}

type Field []int

func (f *Field) Render() {
    fmt.Println("Hello from first")
}

func main() {
    f := First{
        Field{1, 2, 3},
    }
    f.ExampleField.Render()
    renderField("ExampleField", &f)
    renderField2("ExampleField", &f)
}

func renderField(field string, f *First) {
    structVal := reflect.ValueOf(*f)
    renderType := reflect.TypeOf((*Renderer)(nil)).Elem()
    fieldToRender := structVal.FieldByName(field)
    fieldPtr := reflect.PtrTo(fieldToRender.Type())
    fmt.Printf("Implements? %v\n", fieldPtr.Implements(renderType))
    fmt.Printf("Addressable? %v\n", fieldToRender.CanAddr())
    fieldInter := fieldToRender.Interface()
    if renderer, ok := fieldInter.(Renderer); ok {
        // Pointer receiver so this never gets called
        fmt.Print("Able to cast")
        renderer.Render()
    }
}

func renderField2(field string, f *First) {
    structVal := reflect.ValueOf(*f)
    fieldToRender := structVal.FieldByName(field)
    vp := reflect.New(reflect.TypeOf(fieldToRender))
    vp.Elem().Set(reflect.ValueOf(fieldToRender))
    vpAddr := vp.Elem().Addr()
    typeVal := vpAddr.Interface()
    fmt.Println(typeVal) // <main.Field Value>⏎
    renderer := typeVal.(Renderer)
    renderer.Render()
    // interface conversion: *reflect.Value is not main.Renderer: missing method Render
}

renderField2 seems to get me close but Addr() gives me a *Reflect.Value and when I call Interface() that seems to be the underlying type instead. I found reflect value Interface and pointer receiver which seems to be almost exactly what I'm asking, and the question is answered but if I actually call the isZeroer method presented in the playground link it's always false so it doesn't actually seem to answer the question.





Aucun commentaire:

Enregistrer un commentaire