How to use "reflect" to set interface value inside a struct of struct

2.3k views Asked by At

Had a rough time trying to set the interface value by using "reflect" package. The interface value is actually inside a struct of a struct. See my code in Go Playground

Basically, inside initProc, I want to assign dummyAFunc function to DummyA field in Box struct

package main 

import (
    "fmt"
    "reflect"
)

type Box struct {
    Name               string
    DummyA             interface{}
}

type SmartBox struct {
    Box
}

func dummyAFunc(i int) {
    fmt.Println("dummyAFunc() is here!")
}

func initProc(inout interface{}) {
    // Using "inout interface{}", I can take any struct that contains Box struct
    // And my goal is assign dummyAFunc to dummyA in Box struct

    iType:=reflect.TypeOf(inout)
    iValue:=reflect.ValueOf(inout)
    
    fmt.Println("Type & value:", iType.Elem(), iValue.Elem()) // Type & value: *main.SmartBox &{{ <nil>}}

    e := reflect.ValueOf(inout).Elem()
    
    fmt.Println("Can set?", e.CanSet()).      // true
    fmt.Println("NumField", e.NumField())     // panic: reflect: call of reflect.Value.NumField on ptr Value ?????
    fmt.Println("NumMethod", e.NumMethod())   // NumMethod = 0
        
}

func main() {
    smartbox := new (SmartBox)
    initProc(&smartbox)
}

I'm new to Go and I've read the The laws of Reflection but still can't figure it out. Please help. Thanks!

1

There are 1 answers

0
colm.anseo On BEST ANSWER

You are passing a **SmartBix to initProc. So when you dereference once with reflect's Elem() you are still getting a pointer (*Smart box).

Since new already returns a pointer, just use:

smartbox := new (SmartBox)

// InitProc(smartbox) // **SmartBox
InitProc(smartbox) // *SmartBox

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


EDIT

To update the input struct's DummyA field, you can do something like this:

func initProc2(v interface{}) error {

    if reflect.TypeOf(v).Kind() != reflect.Ptr {
        return fmt.Errorf("value must be a pointer")
    }

    dv := reflect.ValueOf(v).Elem()

    if dv.Kind() != reflect.Struct {
        return fmt.Errorf("value must be a pointer to a struct/interface")
    }

    const fname = "DummyA" // lookup field name

    f := dv.FieldByName(fname)

    if !f.CanSet() {
        return fmt.Errorf("value has no field %q or cannot be set", fname)
    }

    nv := reflect.ValueOf(dummyAFunc)

    f.Set(nv)

    return nil
}

Working example: https://play.golang.org/p/VE751GtSGEw