Using reflect to get a pointer to a struct passed as an interface{}

13,536 views
Skip to first unread message

smith.wi...@gmail.com

unread,
Dec 7, 2015, 10:39:44 AM12/7/15
to golang-nuts
I have a use-case where I'm trying to create some middle-ware to mediate between martini-binding and gorp in a generic way.

The struct is passed in by-value so wrap a reflect.Value around a pointer to it (as per the laws of reflection), but I don't seem to be able to convert that into a struct pointer.  I've tried various approaches including using reflect.Value.Addr() and reflect.Value.Convert() on a reflect.PtrTo() of the struct type, all of which panic.

What is the correct strategy to accomplish this?  This is what I'm doing right now -- which I think is the reflect way of doing `return &obj.(*MyStruct)` where obj is an interface{} to a struct:


func to_struct_ptr(obj interface{}) interface{} {

fmt.Println("obj is a", reflect.TypeOf(obj).Name())
// Must take the value on the pointer to the object (see laws of reflection)
v := reflect.ValueOf(&obj)
// Create a pointer type to the underlying element type
ptrtype := reflect.PtrTo(reflect.TypeOf(obj))
fmt.Println("ptrtype is", ptrtype)
// Convert the *interface{} to a *Cat
vp := v.Convert(ptrtype)
// NOTE: `v.Convert(ptrtype)` fails with:
//
//       panic: reflect.Value.Convert: value of type *interface {} cannot be converted to type *main.Cat
//

// Return a `Cat` pointer to obj -- i.e. &obj.(*Cat)
return vp.Interface()
}


See Go playground example:  http://play.golang.org/p/BFWyXnkUuF

Thanks!

James Bardin

unread,
Dec 7, 2015, 10:53:38 AM12/7/15
to golang-nuts, smith.wi...@gmail.com
You almost never need a pointer to an interface

func to_struct_ptr(obj interface{}) interface{} {
vp := reflect.New(reflect.TypeOf(obj))
return vp.Interface()
}

http://play.golang.org/p/UwshHBJPGi

smith.wi...@gmail.com

unread,
Dec 7, 2015, 10:59:39 AM12/7/15
to golang-nuts, smith.wi...@gmail.com
On Monday, December 7, 2015 at 10:53:38 AM UTC-5, James Bardin wrote:
You almost never need a pointer to an interface

func to_struct_ptr(obj interface{}) interface{} {
vp := reflect.New(reflect.TypeOf(obj))
return vp.Interface()
}

http://play.golang.org/p/UwshHBJPGi

That's a new (now empty) Cat though! 

James Bardin

unread,
Dec 7, 2015, 11:16:22 AM12/7/15
to smith.wi...@gmail.com, golang-nuts
Sorry I misunderstood. You can set the element's value once you have a
pointer to the correct type:

func to_struct_ptr(obj interface{}) interface{} {
val := reflect.ValueOf(obj)
vp := reflect.New(val.Type())
vp.Elem().Set(val)
return vp.Interface()
}

Roberto Zanotto

unread,
Dec 7, 2015, 12:38:44 PM12/7/15
to golang-nuts, smith.wi...@gmail.com
If you store a non-pointer inside an interface{} or a reflect.Value, that value will be not settable and you can not get a pointer back safely. The line:

// Must take the value on the pointer to the object (see laws of reflection)
v := reflect.ValueOf(&obj)
is wrong: &obj is a pointer to an interface{} doesn't have (almost) anything to do with your Cat.
You can get a pointer back only if you use something like reflect.New(Cat) or reflect.ValueOf(new(Cat)).
Also note that with (interface{})(Cat{}) (so, not a pointer to Cat, the struct directly), a copy of the Cat is made. Even if you got the pointer out of the interface with unsafe, it would be a pointer to a copy of your Cat.

smith.wi...@gmail.com

unread,
Dec 7, 2015, 1:15:03 PM12/7/15
to golang-nuts, smith.wi...@gmail.com
On Monday, December 7, 2015 at 11:16:22 AM UTC-5, James Bardin wrote:
Sorry I misunderstood. You can set the element's value once you have a
pointer to the correct type:

func to_struct_ptr(obj interface{}) interface{} {
    val := reflect.ValueOf(obj)
    vp := reflect.New(val.Type())
    vp.Elem().Set(val)
    return vp.Interface()
}

I got that working in my example -- here's the final solution:  http://play.golang.org/p/hYnsAijyCE

NOTE: I found that these two are not equivalent

vp.Elem().Set(reflect.ValueOf(obj))  // this works!
vp.Elem().Set(reflect.ValueOf(&obj).Elem()) // panics with "panic: reflect.Set: value of type interface {} is not assignable to type main.Cat"

Many thanks!

-W.

Reply all
Reply to author
Forward
0 new messages