使用反射在Go语言中设置表示空指针的接口的值。

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

Set value of interface representing nil pointer using reflect in Go

问题

我的问题类似于https://stackoverflow.com/questions/45061568/how-to-initialize-struct-pointer-via-reflection。

我有一个表示空指针的接口。我想要更新空指针背后的值。

我从上述问题的答案中的一个示例中获取了代码,但我的情况稍有不同,给定的解决方案不起作用。

InitNilPointer()函数中,值实际上变为&Foo{}。但是在调用处的结果仍然是(*Foo)(nil)。我如何使用反射来将foomain()函数中更新为&Foo{}

情况如下:

func main() {
	var foo *Foo

	InitNilPointer(foo)

    // Want: &main.Foo{A:""}
	fmt.Printf("Want: %+#v\n", &Foo{})

    // Got: (*main.Foo)(nil)
	fmt.Printf("Got: %+#v\n", foo)
}

// InitNilPointer将给定的指针初始化为nil值,然后变为指向某个值的指针。例如,(*Foo)(nil)变为&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()

	// 这只在本地更新了值。调用处仍然有一个nil指针。遗憾。
	rv.Set(reflect.New(t))

	// source: &main.Foo{A:""}
	fmt.Printf("source: %+#v\n", source)
}
英文:

My question is similar to https://stackoverflow.com/questions/45061568/how-to-initialize-struct-pointer-via-reflection.

I have an interface that represents a nil pointer. I want to update the value behind the nil pointer.

I took the code from one of the examples in the answers to the question mentioned above, but my situation is slightly different, and the given solution isn't working.

Inside InitNilPointer() the value actually does become &Foo{}. But the result at the call site remains (*Foo)(nil). How can I use reflect to update foo in main() to &Foo{}?

The situation is the following

func main() {
	var foo *Foo

	InitNilPointer(foo)

    // Want: &main.Foo{A:""}
	fmt.Printf("Want: %+#v\n", &Foo{})

    // Got: (*main.Foo)(nil)
	fmt.Printf("Got: %+#v\n", 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: %+#v\n", source)
}

答案1

得分: 2

无论你将什么传递给函数,都会创建一个副本。如果你传递的是 foo,无论函数做什么,它只能修改副本,而不能修改原始的 foo 变量。

所以你必须传递 &foo(并且修改所指向的对象将修改 foo):

func main() {
    var foo *Foo

    InitNilPointer(&foo)

    fmt.Printf("Want: %+#v\n", &Foo{})
    fmt.Printf("Got: %+#v\n", 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: %+#v\n", source)
}

这将输出(在 Go Playground 上尝试):

source: (*main.Foo)(0xc00000e028)
Want: &main.Foo{A:""}
Got: &main.Foo{A:""}
英文:

Whatever you pass to a function, a copy is made. If you pass foo, no matter what the function does, it can only modify the copy but not the original foo variable.

So you must pass &foo (and modifying the pointed object will modify foo):

func main() {
	var foo *Foo

	InitNilPointer(&foo)

	fmt.Printf("Want: %+#v\n", &Foo{})
	fmt.Printf("Got: %+#v\n", 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: %+#v\n", source)
}

This will output (try it on the Go Playground):

source: (**main.Foo)(0xc00000e028)
Want: &main.Foo{A:""}
Got: &main.Foo{A:""}

huangapple
  • 本文由 发表于 2022年2月3日 17:28:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/70968737.html
匿名

发表评论

匿名网友

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

确定