0

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

1 Answer 1

3

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
}
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.