panic: reflect: call of reflect.Value.FieldByName on interface Value

Viewed 7325

I have a variable of type interface{} and I want to change the value of a field using reflection. How can I do it? Variable must be of type interface{} due to other requirements. If the variable isn't of type interface{} all works, otherwise code throws

reflect: call of reflect.Value.FieldByName on interface Value

my code

package main

import (
    "fmt"
    "reflect"
)

func main() {
    a := struct {
        Name string
    }{}

    // works
    reflect.ValueOf(&a).Elem().FieldByName("Name").SetString("Hello")
    fmt.Printf("%#v\n", a)

    var b interface{}
    b = struct {
        Name string
    }{}
    // panics
    reflect.ValueOf(&b).Elem().FieldByName("Name").SetString("Hello")
    fmt.Printf("%#v\n", b)
}

2 Answers

The application must call Elem() twice to get the struct value:

reflect.ValueOf(&b).Elem().Elem().FieldByName("Name").SetString("Hello")

The first call Elem() dereferences the pointer to interface{}. The second call to Elem() gets the value contained in the interface.

With this change, the panic is reflect.Value.SetString using unaddressable value.

The application cannot set fields directly on the struct value contained in the interface because values contained in an interface are not addressable.

Copy the struct value to a temporary variable, set the field in the temporary variable and copy the temporary variable back to the interface.

var b interface{}
b = struct {
    Name string
}{}

// v is the interface{}
v := reflect.ValueOf(&b).Elem()

// Allocate a temporary variable with type of the struct.
//    v.Elem() is the vale contained in the interface.
tmp := reflect.New(v.Elem().Type()).Elem()

// Copy the struct value contained in interface to
// the temporary variable.
tmp.Set(v.Elem())

// Set the field.
tmp.FieldByName("Name").SetString("Hello")

// Set the interface to the modified struct value.
v.Set(tmp)

fmt.Printf("%#v\n", b)

Run it on the Go playground.

The interface b is initialized using the value of the anonymous struct, so b contains a copy of the struct, and the values are not addressable. Initialize b using a pointer:

var b interface{}
    b = &struct {
        Name string
    }{}
    reflect.ValueOf(b).Elem().FieldByName("Name").SetString("Hello")
Related