4

I have a struct like this one:

type ProductionInfo struct {
    StructA []Entry
}

type Entry struct {
    Field1 string
    Field2 int
}

I would like to change the value of Field1 using reflection but the reflect object returned always CanSet() = false. What can I do? See playground example.

https://play.golang.org/p/eM_KHC3kQ5

Here is the code:

func SetField(source interface{}, fieldName string, fieldValue string) {
    v := reflect.ValueOf(source)
    tt := reflect.TypeOf(source)

    for k := 0; k < tt.NumField(); k++ {
        fieldValue := reflect.ValueOf(v.Field(k))

        fmt.Println(fieldValue.CanSet())
        if fieldValue.CanSet() {
            fieldValue.SetString(fieldValue.String())
        }
    }
}

func main() {
    source := ProductionInfo{}
    source.StructA = append(source.StructA, Entry{Field1: "A", Field2: 2})

    SetField(source, "Field1", "NEW_VALUE")
}

1 Answer 1

6

Multiple errors. Let's iterate over them.

First, you pass a value of ProductionInfo and not a value of Entry whose field you want to modify, so first change it to:

SetField(source.StructA[0], "Field1", "NEW_VALUE")

Next, you are passing a (non-pointer) value. You can't modify the fields of a non-pointer struct with reflection, because that would only modify a copy which would be discarded. In order to avoid this (and further confusion), this is not allowed (CanSet() returns false). So you have to pass a pointer to the struct:

SetField(&source.StructA[0], "Field1", "NEW_VALUE")

Now inside SetField() the reflect.ValueOf(source) will describe the passed pointer. You may use Value.Elem() to navigate to the reflect.Value of the pointed object (the struct value):

v := reflect.ValueOf(source).Elem()

And now it works. Modified code:

func SetField(source interface{}, fieldName string, fieldValue string) {
    v := reflect.ValueOf(source).Elem()

    fmt.Println(v.FieldByName(fieldName).CanSet())

    if v.FieldByName(fieldName).CanSet() {
        v.FieldByName(fieldName).SetString(fieldValue)
    }
}

func main() {
    source := ProductionInfo{}
    source.StructA = append(source.StructA, Entry{Field1: "A", Field2: 2})

    fmt.Println("Before: ", source.StructA[0])
    SetField(&source.StructA[0], "Field1", "NEW_VALUE")
    fmt.Println("After: ", source.StructA[0])
}

Output (try it on the Go Playground):

Before:  {A 2}
true
After:  {NEW_VALUE 2}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for tips Icza. I know there was errors during assegnation because i tried to create "dummy" code for example and I did it in a very short time :) . BTW I got where my erorrs were. Thank you

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.