我的问题类似于如何通过反射初始化结构指针。
我有一个表示零指针的接口。我想更新nil指针后面的值。
我从上面提到的问题的答案中的一个例子中获取了代码,但我的情况略有不同,并且给定的解决方案不起作用。
在InitNilPointer()
内部,该值实际上变为&Foo{}
。但是在呼叫站点的结果仍然是(*Foo)(nil)
。如何使用反射将main()
中的foo
更新为&Foo{}
?
情况如下
func main() {
var foo *Foo
InitNilPointer(foo)
// Want: &main.Foo{A:""}
fmt.Printf("Want: %+#vn", &Foo{})
// Got: (*main.Foo)(nil)
fmt.Printf("Got: %+#vn", foo)
}
// InitNilPointer initiates the given pointer to a nil value to become a pointer to
// a value. E.g. (*Foo)(nil) becomes &Foo{}.
func InitNilPointer(source interface{}) {
v := reflect.ValueOf(source)
if reflect.Indirect(v).IsValid() {
return
}
rv := reflect.ValueOf(&source).Elem()
t := rv.Elem().Type().Elem()
// This only updates the value locally. The call site still has a nil pointer. SAD.
rv.Set(reflect.New(t))
// source: &main.Foo{A:""}
fmt.Printf("source: %+#vn", source)
}
无论传递给函数什么,都会生成一个副本。如果传递foo
,无论函数做什么,它都只能修改副本,而不能修改原始foo
变量。
因此,必须通过&foo
(修改指向的对象将修改foo
(:
func main() {
var foo *Foo
InitNilPointer(&foo)
fmt.Printf("Want: %+#vn", &Foo{})
fmt.Printf("Got: %+#vn", foo)
}
func InitNilPointer(source interface{}) {
rv := reflect.ValueOf(source).Elem()
if reflect.Indirect(rv).IsValid() {
return
}
t := rv.Type().Elem()
rv.Set(reflect.New(t))
// source: &main.Foo{A:""}
fmt.Printf("source: %+#vn", source)
}
这将输出(在Go Playground上尝试(:
source: (**main.Foo)(0xc00000e028)
Want: &main.Foo{A:""}
Got: &main.Foo{A:""}