简体   繁体   中英

Golang reflection: Can't set fields of interface wrapping a struct

I am trying to implement a method that changes the value of fields in an object that can have an arbitrary structure. The traversion of the fields is no problem when I have the pointer to a struct. But I can not manage to change the fields when I have an interface that does not wrap a pointer to a struct but the struct itself, in short:

// The following doesn't work
var x interface{} = A{Str: "Hello"}
// This panics: reflect: call of reflect.Value.Field on ptr Value
reflect.ValueOf(&x).Field(0).SetString("Bye")
// This panics: reflect: call of reflect.Value.Field on interface Value
reflect.ValueOf(&x).Elem().Field(0).SetString("Bye")
// This panics: reflect: reflect.Value.SetString using unaddressable value
reflect.ValueOf(&x).Elem().Elem().Field(0).SetString("Bye")
// This prints `false`. But I want this to be settable
fmt.Println(reflect.ValueOf(&x).Elem().Elem().Field(0).CanSet())

// This works
var z interface{} = &A{Str: "Hello"}
// This prints `true`
fmt.Println(reflect.ValueOf(z).Elem().Field(0).CanSet())

In long: http://play.golang.org/p/OsnCPvOx8F

I have read The Laws of Reflection so I am aware that I may only modify fields when I have a pointer to a struct. So my question is now: How do I get the pointer to the data of the struct?

UPDATE:

I got it working using basically y := reflect.New(reflect.TypeOf(x)) so the values of y are settable now. For an extensive example see this: https://gist.github.com/hvoecking/10772475

You appear to be trying to modify the dynamic value stored inside an interface variable. The only operations you can perform on an interface variable are to get or set the dynamic value (operations that make copies), and to check the type of the stored value.

To understand why things are this way, imagine that there was such an operation and we had the following code:

var ptr *A = pointer_to_dynamic_value(x)
x = B{...}

What does ptr now represent? The language is free to reuse storage when assigning new values to an interface variable, so the the ptr might now point to the memory for the B value, which breaks the type safety of the language (with the current compilers storage is only guaranteed to be reused for small values, but the point remains).

The only safe way to mutate the value stored in an interface is to copy the value out, then assign back a the modified version. For example:

a := x.(A)
a.Str = "Bye"
x = a

The reflect package reflects these restrictions, so the reflect.Value representing the field of the dynamic value is considered read only.

You are able to set fields in your first example because the dynamic value for z is a *A pointer rather than the struct itself: this means the referenced struct can be modified.

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