问题
Code below will raise a runtime error when append reflect.Value
of nil
:
package main
import (
"fmt"
"reflect"
)
func main() {
var list []interface{}
v := reflect.ValueOf(list)
v = reflect.Append(v, reflect.ValueOf(1)) // [1]
v = reflect.Append(v, reflect.ValueOf("1")) // [1, 1]
v = reflect.Append(v, reflect.ValueOf(nil)) // runtime error
fmt.Println(v)
}
So
- why there is a runtime error?
- how can I use
reflect.Append
to add anil
tointerface{}
slice?
回答1:
interface{}
is an interface type, and they are "tricky". They are wrappers around a concrete value and the concrete type, schematically a (value, type) pair.
So when you pass a concrete value to a function that expects an interface{}
value, the concrete value will be wrapped in an interface{}
value automatically, implicitly. If you pass a nil
to such a function, the interface value itself will be nil
. If you pass a nil
pointer to it, such as (*int)(nil)
, the interface value will not be nil
but an interface value holding "(nil, *int)".
If you pass nil
to reflect.ValueOf()
, it results in a "zero" reflect.Value
which represents no value at all. If you pass this to reflect.Append()
, it will not have the type information, it will not know what you want to append to the slice.
It is possible to create a value that represents the nil
interface value.
To do that, we may start from the type descriptor of a value of an interface pointer (pointers to interface rarely makes sense, but this is one of them). We navigate to the type descriptor of the pointed type, which is interface{}
. We obtain a zero value of this type (using reflect.Zero()), which is nil
(zero value of interface types is nil
).
Zero returns a Value representing the zero value for the specified type. The result is different from the zero value of the Value struct, which represents no value at all.
So this is how it looks like:
typeOfEmptyIface := reflect.TypeOf((*interface{})(nil)).Elem()
valueOfZeroEmptyIface := reflect.Zero(typeOfEmptyIface)
v = reflect.Append(v, valueOfZeroEmptyIface)
Or as a single line:
v = reflect.Append(v, reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()))
To check the results, let's use:
fmt.Printf("%#v\n", v)
And also let's type-assert back the slice, and add a nil
value using the builtin append()
function:
list = v.Interface().([]interface{})
list = append(list, nil)
fmt.Printf("%#v\n", list)
Let's do an explicit, extra check if the elements are nil
(compare them to nil
). Although using %#v
verb this is redundant, %v
likes to print non-nil
interfaces holding nil
concrete values just as nil
(the same as if the interface value itself would be nil
).
fmt.Println(list[2] == nil, list[3] == nil)
Ouptut will be (try it on the Go Playground):
[]interface {}{1, "1", interface {}(nil)}
[]interface {}{1, "1", interface {}(nil), interface {}(nil)}
true true
See related question: Hiding nil values, understanding why golang fails here
Also: The Go Blog: The Laws of Reflection
来源:https://stackoverflow.com/questions/56396510/how-to-append-nil-to-dynamic-type-slice-by-reflect-append