1

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!

calvinchso
  • 15
  • 5
  • `new` returns a **pointer** to an instance of the given type, and `&` also returns a **pointer** to the type of the expression it preceeds. So you're actually passing `**SmartBox` to `initProc`. Doing `Elem` on that value only once will give you `*SmartBox`, and pointers don't have fields, only the structs they point to have fields, so you cannot call `NumField` on a pointer. – mkopriva May 02 '21 at 02:32
  • From the above the solution should be obvious, either use `Elem().Elem()` or pass only `*SmartBox` instead of `**SmartBox` to `initProc`. – mkopriva May 02 '21 at 02:33
  • Thanks @mkopriva. It saves a day for a Go newbie like me! – calvinchso May 02 '21 at 07:10

1 Answers1

1

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

colm.anseo
  • 19,337
  • 4
  • 43
  • 52
  • Thanks @colm.anseo. I've modified it such that it is able to access the fields in ```Box``` https://play.golang.org/p/Be8wXYiOe6G But how about setting the ```Box's DummyA``` field value to ```dummyAFunc```? – calvinchso May 02 '21 at 07:11
  • @calvinchso I've updated the answer with field-update code. – colm.anseo May 02 '21 at 16:05