简体   繁体   中英

How to set a struct member that is a pointer to an arbitrary value using reflection

Note: I want to do the same as How to set a struct member that is a pointer to a string using reflection in Go , but in a more generic way. The existing question does not solve my problem.

I have structs with different kinds of fields that I want to populate using reflection:

type MyStruct struct {
    SomeInt       int
    SomeString    string
    SomeIntPtr    *int
    SomeStringPtr *string
}

The value I want to write into the individual fields is retrieved from a configuration-store and parsed into the correct type, similar to this:

func getValueForField(fieldName string) interface{}

For int and *int types, the function returns an int (wrapped in an interface). For string and *string , the function returns string (behind an interface) and so on, for all types.

--> Note that it does NOT return *int / *string !

And now I want to assign the value to the struct fields:

var field reflect.Value  = reflect.ValueOf(ptrToMyStruct).Elem().Field(i)
var value interface{}    = getValueForField(....)
var isPointer bool       = field.Kind() == reflect.Ptr

// assign "value" to "field":
if isPointer {
    // ??
    field.Set(reflect.ValueOf(value).Addr()) // panic: reflect.Value.Addr of unaddressable value
} else {
    field.Set(reflect.ValueOf(value)) // works
}

Assigning those values to concrete types is easy and works as expected. But I can't assign an int type (returned from getValueForField ) to an *int field without somehow getting an address. And since I only have an interface{} , this needs to be done via reflection.

Here's a link to the Go Playground: https://play.golang.org/p/zElEGHgx1IO

Use reflect.New() to construct a new pointer value for the field, set the pointed value from value , and then set this new value to the pointer field.

For example:

type MyStruct struct {
    SomeIntPtr    *int
    SomeStringPtr *string
}

var ms MyStruct

// Set int pointer
{
    var i interface{} = 3 // of type int

    f := reflect.ValueOf(&ms).Elem().FieldByName("SomeIntPtr")
    x := reflect.New(f.Type().Elem())
    x.Elem().Set(reflect.ValueOf(i))
    f.Set(x)
}

// Set string pointer
{
    var i interface{} = "hi" // of type string

    f := reflect.ValueOf(&ms).Elem().FieldByName("SomeStringPtr")
    x := reflect.New(f.Type().Elem())
    x.Elem().Set(reflect.ValueOf(i))
    f.Set(x)
}

fmt.Println("ms.SomeIntPtr", *ms.SomeIntPtr)
fmt.Println("ms.SomeStringPtr", *ms.SomeStringPtr)

This will output (try it on the Go Playground ):

ms.SomeIntPtr 3
ms.SomeStringPtr hi

So your code may look like this:

// assign "value" to "field":
if isPointer {
    x := reflect.New(field.Type().Elem())
    x.Elem().Set(reflect.ValueOf(value))
    field.Set(x)
} else {
    field.Set(reflect.ValueOf(value)) // works
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM