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 想问一下树莓派接上显示屏后出现如图所示画面,是什么问题导致的
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥15 cmd cl 0x000007b
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
  • ¥500 火焰左右视图、视差(基于双目相机)
  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号