【问题标题】:how to append nil to dynamic type slice by reflect.Append如何通过 reflect.Append 将 nil 附加到动态类型切片
【发布时间】:2019-10-17 04:30:09
【问题描述】:

当附加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)
}

所以

  • 为什么会出现运行时错误?
  • 如何使用reflect.Appendnil 添加到interface{} 切片?

【问题讨论】:

标签: go reflection null slice


【解决方案1】:

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

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

如果您将nil 传递给reflect.ValueOf(),它会产生一个“零”reflect.Value,它表示根本没有任何值。如果你把它传递给reflect.Append(),它就没有类型信息,它不会知道你想在切片上附加什么。

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

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

Zero 返回一个 Value,表示指定类型的零值。结果与 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)

还让我们对切片进行类型断言,并使用内置的append() 函数添加一个nil 值:

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

【讨论】:

    猜你喜欢
    • 2016-05-25
    • 1970-01-01
    • 2016-10-16
    • 2016-02-22
    • 2016-11-27
    • 1970-01-01
    • 2017-09-09
    • 2015-11-09
    • 2016-10-20
    相关资源
    最近更新 更多