如何使用反射将一个包含 nil 的指针设置为某个值?

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

How do you set a value to a pointer containing nil using reflection

问题

你在尝试将一个值设置给一个结构体中的空指针,代码如下:

// https://play.golang.org/p/jPTMNC_ZQ9
package main

import (
	"fmt"
	"reflect"
)

type T struct {
	A *int
}

func main() {
	fmt.Println("Hello, playground")
	t := &T{}
	
	v := 1
	vptr := &v
	
	CopyValue(vptr, t.A) // 我想将t.A设置为1
}

func CopyValue(src interface{}, dest interface{}) {
	srcRef := reflect.ValueOf(src)
	if srcRef.Kind() == reflect.Ptr {
		srcRef = srcRef.Elem()
	}

	destRef := reflect.New(srcRef.Type()).Elem()
	destRef.Set(srcRef)
	reflect.ValueOf(dest).Elem().Set(destRef)
}

然而,你遇到了以下错误:

panic: reflect: call of reflect.Value.Set on zero Value

goroutine 1 [running]:
reflect.flag.mustBeAssignable(0x0, 0x1040a128)
	/usr/local/go/src/reflect/value.go:221 +0x260
reflect.Value.Set(0x0, 0x0, 0x0, 0xdefc0, 0x1040a128, 0x182)
	/usr/local/go/src/reflect/value.go:1339 +0x40
main.CopyValue(0xd7860, 0x1040a124, 0xd7860, 0x0)
	/tmp/sandbox487854080/main.go:30 +0x1a0
main.main()
	/tmp/sandbox487854080/main.go:19 +0x100

我做错了什么?

英文:

I'm trying to set a value to a nil pointer in a struct like so.

// https://play.golang.org/p/jPTMNC_ZQ9
package main

import (
	"fmt"
	"reflect"
)

type T struct {
	A *int
}

func main() {
	fmt.Println("Hello, playground")
	t := &T{}
	
	v := 1
	vptr := &v
	
	CopyValue(vptr, t.A) // I want to set t.A to contain 1
}

func CopyValue(src interface{}, dest interface{}) {
	srcRef := reflect.ValueOf(src)
	if srcRef.Kind() == reflect.Ptr {
		srcRef = srcRef.Elem()
	}

	destRef := reflect.New(srcRef.Type()).Elem()
	destRef.Set(srcRef)
	reflect.ValueOf(dest).Elem().Set(destRef)
}

However, I encounter the following error:

panic: reflect: call of reflect.Value.Set on zero Value

goroutine 1 [running]:
reflect.flag.mustBeAssignable(0x0, 0x1040a128)
	/usr/local/go/src/reflect/value.go:221 +0x260
reflect.Value.Set(0x0, 0x0, 0x0, 0xdefc0, 0x1040a128, 0x182)
	/usr/local/go/src/reflect/value.go:1339 +0x40
main.CopyValue(0xd7860, 0x1040a124, 0xd7860, 0x0)
	/tmp/sandbox487854080/main.go:30 +0x1a0
main.main()
	/tmp/sandbox487854080/main.go:19 +0x100

What am I doing wrong?

答案1

得分: 5

为了能够修改t.A指向的内容,你需要将其引用传递给CopyValue函数。

CopyValue(vptr, &t.A) //(注意 & 符号)

然后你可以将指针赋值给新的地址

func CopyValue(src interface{}, dest interface{}) {
    srcRef := reflect.ValueOf(src)
    vp := reflect.ValueOf(dest)
    vp.Elem().Set(srcRef)
}

参考这里的第三个反射定律”:https://blog.golang.org/laws-of-reflection

完整的工作代码

package main

import (
    "fmt"
    "reflect"
)

type T struct {
    A *int
}

func main() {
    t := &T{}
    v := 1
    vptr := &v
    CopyValue(vptr, &t.A) // 我们传递了对 t.A 的引用,因为我们想要修改它
    fmt.Printf("%v\n", *t.A)
}

func CopyValue(src interface{}, dest interface{}) {
    srcRef := reflect.ValueOf(src)
    vp := reflect.ValueOf(dest)
    vp.Elem().Set(srcRef)
}
英文:

In order to be able to modify what t.A points to, you need to send a reference to it to your CopyValue function.

CopyValue(vptr, &t.A) // (note the &)

You can then assign the pointer to the new address:

func CopyValue(src interface{}, dest interface{}) {
    srcRef := reflect.ValueOf(src)
    vp := reflect.ValueOf(dest)
    vp.Elem().Set(srcRef)
}

See the 3rd "law of reflection" here: https://blog.golang.org/laws-of-reflection

Full working code:

package main

import (
	"fmt"
	"reflect"
)

type T struct {
	A *int
}

func main() {
	t := &T{}
	v := 1
	vptr := &v
	CopyValue(vptr, &t.A) // we pass a reference to t.A since we want to modify it
	fmt.Printf("%v\n", *t.A)
}

func CopyValue(src interface{}, dest interface{}) {
	srcRef := reflect.ValueOf(src)
	vp := reflect.ValueOf(dest)
	vp.Elem().Set(srcRef)
}

答案2

得分: 1

reflect.ValueOf(dest).Elem().Set(destRef)这一行代码中,reflect.ValueOf(dest)会返回nil,因为你传入了一个nil指针。在这个nil指针上调用.Elem()是无效的,因为nil指针没有元素。

英文:

reflect.ValueOf(dest).Elem().Set(destRef)

If you look into this line, reflect.ValueOf(dest) will give you nil, since you passed in a nil pointer. Calling .Elem() on this is invalid, since there is no element to the nil pointer.

答案3

得分: 1

t.A在传入时是一个空指针,所以CopyValue被要求将一个值复制到一个无效(空)的位置。你需要为它分配一个int类型的空间,然后CopyValue就能够将值复制到指向的位置。

以下代码解决了这个错误并允许值被复制:

t := &T{}
t.A = new(int) // 添加这一行
英文:

t.A is a nil pointer when you pass it in, so CopyValue is being asked to copy a value into an invalid (nil) location. You need to allocate space for an int for it to point to, and then CopyValue will be able to do the copy into the location pointed at.

This resolves the error and allows the value to be copied:

t := &T{}
t.A = new(int) // Add this line

huangapple
  • 本文由 发表于 2017年7月17日 07:14:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/45134127.html
匿名

发表评论

匿名网友

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

确定