Ben Guild
Ben Guild

Reputation: 5126

Can't get reflect to not return pointers, compiler panics on conversion?

Code written around something like this is causing an issue:

func CreateNewItemOfType(returnType reflect.Type) (interface {}) {
    return reflect.New(returnType).Interface();

}

... How can one actually return a struct of returnType and not a pointer to a struct, as reflect creates here? The compiler builds this fine but panics at run-time, yet will not accept an asterisk in front of the return call here in order to actually return the struct and not a pointer.

Upvotes: 1

Views: 54

Answers (1)

icza
icza

Reputation: 418227

reflect.New() creates a new value of the specified type, and returns the reflect.Value descriptor of a pointer to that value. You may use Value.Elem() to "navigate" from pointer to pointed value wrapped in a reflect.Value. Then you can call Value.Interface() to get the value (struct) as an interface{} value.

If you need a value of concrete type, you may use type assertion to "extract" the value wrapped in the interface{} value.

func CreateNewItemOfType(returnType reflect.Type) interface{} {
    return reflect.New(returnType).Elem().Interface()
}

Testing it:

type Point struct {
    X, Y int
}

t := reflect.TypeOf(Point{})

i := CreateNewItemOfType(t) // i is of type interface{}
fmt.Printf("%T %+v\n", i, i)

pt := i.(Point) // pt is of type Point
fmt.Printf("%T %+v\n", pt, pt)

Output (try it on the Go Playground):

main.Point {X:0 Y:0}
main.Point {X:0 Y:0}

Note:

It is also possible to obtain the non-pointer struct value if Value.Elem() is not used. For that, you need to type-assert the pointer value (of type *Point), then you can dereference the pointer to get the non-pointer.

See this example:

t := reflect.TypeOf(Point{})
pi := reflect.New(t).Interface() // pi is of type interface{}
fmt.Printf("%T %+v\n", pi, pi)

ppt := pi.(*Point) // pt is of type *Point
fmt.Printf("%T %+v\n", ppt, ppt)

i2 := *ppt // i2 is of type Point
fmt.Printf("%T %+v\n", i2, i2)

Output:

*main.Point &{X:0 Y:0}
*main.Point &{X:0 Y:0}
main.Point {X:0 Y:0}

Upvotes: 3

Related Questions