dongqie2355 2019-05-31 13:58
浏览 137
已采纳

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

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 a nil to interface{} slice?
  • 写回答

1条回答 默认 最新

  • duanqian6982 2019-05-31 14:22
    关注

    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
    ", 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
    ", 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

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 ATAC测序到底用什么peak文件做Diffbind差异分析
  • ¥15 安装ubantu过程中第一个vfat 文件挂载失败
  • ¥20 GZ::CTF如何兼容一些靶机?
  • ¥15 etcd集群部署问题
  • ¥20 谁可以帮我一下问一下各位
  • ¥15 为何重叠加权后love图的SMD与svyCreateTableOne函数绘制基线表的不一致
  • ¥15 QFILHelper怎么恢复全字库,提示进程已完成,只能恢复分区文件
  • ¥150 求 《小魔指》街机游戏机整合模拟软件
  • ¥20 你好,我想问下easyExcel下拉多选,或者复选框可以实现吗
  • ¥20 双非跨考工科哪个专业和方向就业前景好?