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

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

报告相同问题?

悬赏问题

  • ¥20 关于#stm32#的问题:需要指导自动酸碱滴定仪的原理图程序代码及仿真
  • ¥20 设计一款异域新娘的视频相亲软件需要哪些技术支持
  • ¥15 stata安慰剂检验作图但是真实值不出现在图上
  • ¥15 c程序不知道为什么得不到结果
  • ¥40 复杂的限制性的商函数处理
  • ¥15 程序不包含适用于入口点的静态Main方法
  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因