如何通过reflect.Append将nil附加到动态类型切片

时间:2019-05-31 13:58:18

标签: go reflection null slice

以下代码会在附加reflect.Value的{​​{1}}时引发运行时错误:

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) } reflect.Append切片添加nil

1 个答案:

答案 0 :(得分:4)

interface{}是一种接口类型,它们是“ tricky”。它们是具体值和具体类型的包装,示意性地是(值,类型)对。

因此,当您将具体值传递给需要interface{}值的函数时,具体值将自动隐式地包装在interface{}值中。如果将nil传递给此类函数,则接口值本身将为nil。如果您传递一个指向nil的指针,例如(*int)(nil),则该接口值将不是nil,而是一个包含“(nil,* int)”的接口值。

如果将nil传递给reflect.ValueOf(),将导致一个“零” reflect.Value,它根本不代表任何值。如果将其传递给reflect.Append(),它将没有类型信息,也将不知道要追加到切片的内容。

可以创建一个表示nil接口值的值。

为此,我们可以从接口指针的值的类型描述符开始(指向接口很少的指针很有意义,但这就是其中之一)。我们导航到指向类型的类型描述符,即interface{}。我们获得这种类型的零值(使用reflect.Zero()),即nil(接口类型的零值是nil)。

  

零返回一个值,该值表示指定类型的零值。结果与Value结构的零值不同,后者根本不代表任何值。

这就是它的样子:

typeOfEmptyIface := reflect.TypeOf((*interface{})(nil)).Elem()
valueOfZeroEmptyIface := reflect.Zero(typeOfEmptyIface)
v = reflect.Append(v, valueOfZeroEmptyIface)

或作为一行:

v = reflect.Append(v, reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()))

要检查结果,请使用:

fmt.Printf("%#v\n", v)

还要让我们键入断言的切片,并使用内置的nil函数添加一个append()值:

list = v.Interface().([]interface{})
list = append(list, nil)
fmt.Printf("%#v\n", list)

让我们进行显式的额外检查,检查元素是否为nil(将它们与nil进行比较)。尽管使用%#v动词是多余的,但%v喜欢打印包含nil具体值的非nil接口,就像nil一样(与接口一样值本身就是nil)。

fmt.Println(list[2] == nil, list[3] == nil)

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

[]interface {}{1, "1", interface {}(nil)}
[]interface {}{1, "1", interface {}(nil), interface {}(nil)}
true true

查看相关问题:Hiding nil values, understanding why golang fails here

也:The Go Blog: The Laws of Reflection