Golang反射:无法设置包装结构的接口字段。

huangapple go评论85阅读模式
英文:

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

问题

我正在尝试实现一种方法,可以更改具有任意结构的对象中字段的值。当我有指向结构体的指针时,遍历字段没有问题。但是,当我有一个不包装指向结构体的指针而是结构体本身的接口时,我无法成功更改字段的值,简而言之:

// 以下代码无法工作
var x interface{} = A{Str: "Hello"}
// 这会引发错误:reflect: call of reflect.Value.Field on ptr Value
reflect.ValueOf(&x).Field(0).SetString("Bye")
// 这会引发错误:reflect: call of reflect.Value.Field on interface Value
reflect.ValueOf(&x).Elem().Field(0).SetString("Bye")
// 这会引发错误:reflect: reflect.Value.SetString using unaddressable value
reflect.ValueOf(&x).Elem().Elem().Field(0).SetString("Bye")
// 这会打印 `false`。但我希望它是可设置的
fmt.Println(reflect.ValueOf(&x).Elem().Elem().Field(0).CanSet())

// 以下代码可以工作
var z interface{} = &A{Str: "Hello"}
// 这会打印 `true`
fmt.Println(reflect.ValueOf(z).Elem().Field(0).CanSet())

详细代码请参考:http://play.golang.org/p/OsnCPvOx8F

我已经阅读了《反射定律》The Laws of Reflection,所以我知道只有在有指向结构体的指针时才能修改字段。所以我的问题是:如何获取结构体数据的指针?

更新:

我通过基本使用 y := reflect.New(reflect.TypeOf(x)) 来使其工作,这样 y 的值现在是可设置的。详细示例请参考:https://gist.github.com/hvoecking/10772475

英文:

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

答案1

得分: 10

您似乎正在尝试修改接口变量中存储的动态值。在接口变量上,您只能执行获取或设置动态值(进行副本操作)以及检查存储值的类型的操作。

为了理解为什么会这样,想象一下如果存在这样的操作,并且我们有以下代码:

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

现在,ptr代表什么?语言在将新值分配给接口变量时可以重用存储空间,因此ptr现在可能指向B值的内存,这会破坏语言的类型安全性(在当前的编译器中,存储空间仅保证对小值进行重用,但观点仍然成立)。

唯一安全的方式是修改存储在接口中的值是先将值复制出来,然后将修改后的版本重新赋值。例如:

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

reflect包反映了这些限制,因此被认为是只读的reflect.Value表示动态值的字段。

您能够在第一个示例中设置字段,是因为z的动态值是*A指针,而不是结构本身:这意味着可以修改引用的结构。

英文:

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.

huangapple
  • 本文由 发表于 2014年4月13日 21:46:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/23043510.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定