1

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)
}

xaos_xv
  • 597
  • 1
  • 7
  • 21
qwerzxcv
  • 29
  • 2

2 Answers2

4

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.

I Love Reflection
  • 2,124
  • 2
  • 9
1

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")
Burak Serdar
  • 27,223
  • 3
  • 14
  • 34
  • I need solution for both `b = &struct{}` and `b = struct{}`. Any idea how can I achieve it? – qwerzxcv Aug 15 '20 at 10:36
  • > The interface b is initialized using the value of the anonymous struct anonymous struct it's only example, it doesn't work for normal struct as well – qwerzxcv Aug 15 '20 at 10:38
  • 1
    You cannot set the value of a member of a struct if you have a pointer to a copy of that struct. That is what the varisble b is. See what happens if you did ValoeOf(a) in your first case. – Burak Serdar Aug 15 '20 at 13:50