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?
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
", *t.A)
}
func CopyValue(src interface{}, dest interface{}) {
srcRef := reflect.ValueOf(src)
vp := reflect.ValueOf(dest)
vp.Elem().Set(srcRef)
}
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.
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