Go 优雅判断 interface 是否为 nil

TimLiuDream · 2024-01-15 14:47:11 · 1974 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2024-01-15 14:47:11 的主题,其中的信息可能已经有所发展或是发生改变。

关注公众号【爱发白日梦的后端】分享技术干货、读书笔记、开源项目、实战经验、高效开发工具等,您的关注将是我的更新动力!

背景

很久之前发过一篇文章:《10个令人惊叹的Go语言技巧,让你的代码更加优雅》,这篇文章中第八点有一处错误的地方被认真的读者发现了:

Snipaste_2024-01-15_00-22-06.png

于是我有空之后,立马重新看了那篇文章的内容,确实是存在读者所说的问题。

问题

问题就在于下面这句话,文章也是有列出的:

即使接口持有的值为 nil,也不意味着接口本身为 nil

但是在执行以下语句的时候,是有可能报 panic 的:

return reflect.ValueOf(x).IsNil()

而输出也是非常明显的指出错误:

panic: reflect: call of reflect.Value.IsNil on int Value

因为不可 nilinterface 是不能使用 reflect.Value.IsNil 方法。

那么问题就很好解决了。

解决方式

我们在执行 reflect.Value.IsNil 方法之前,进行一次判断是否为指针即可:

func IsNil(x interface{}) bool {
    if x == nil {
        return true
    }
    rv := reflect.ValueOf(x)
    return rv.Kind() == reflect.Ptr && rv.IsNil()
}

重点在于 rv.Kind() == reflect.Ptr && rv.IsNil() 这段代码。

这段代码的作用:

  • 判断 x 的类型是否为指针。
  • 判断 x 的值是否真的为 nil

下面我们使用几种常见的数据类型来进行测试:

func IsNil(x interface{}) bool {
    if x == nil {
        return true
    }
    rv := reflect.ValueOf(x)
    return rv.Kind() == reflect.Ptr && rv.IsNil()
}

func main() {
    fmt.Printf("int IsNil: %t\n", IsNil(returnInt()))
    fmt.Printf("intPtr IsNil: %t\n", IsNil(returnIntPtr()))
    fmt.Printf("slice IsNil: %t\n", IsNil(returnSlice()))
    fmt.Printf("map IsNil: %t\n", IsNil(returnMap()))
    fmt.Printf("interface IsNil: %t\n", IsNil(returnInterface()))
    fmt.Printf("structPtr IsNil: %t\n", IsNil(returnStructPtr()))
}

func returnInt() interface{} {
    var value int
    return value
}

func returnIntPtr() interface{} {
    var value *int
    return value
}

func returnSlice() interface{} {
    var value []string
    return value
}

func returnMap() interface{} {
    var value map[string]struct{}
    return value
}

func returnInterface() interface{} {
    var value interface{}
    return value
}

type People struct {
    Name string
}

func returnStructPtr() interface{} {
    var value *People
    return value
}

我们先后使用了 int*intslicemapinterface{}自定义结构体 来测试此 IsNil 方法。运行程序输出为:

int IsNil: false
intPtr IsNil: true
slice IsNil: false
map IsNil: false
interface IsNil: true
structPtr IsNil: true

从测试结果来看,目前是符合我们对此方法的定位的。


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1974 次点击  ∙  2 赞  
加入收藏 微博
4 回复  |  直到 2024-02-06 20:04:54
YuPeng
YuPeng · #1 · 大约1年之前
IsNil reports whether its argument v is nil. The argument must be a chan, func, interface, map, pointer, or slice value; if it is not, IsNil panics. Note that IsNil is not always equivalent to a regular comparison with nil in Go. For example, if v was created by calling ValueOf with an uninitialized interface variable i, i==nil will be true but v.IsNil will panic as v will be the zero Value.
func (reflect.Value).IsNil() bool
GahdWu
GahdWu · #2 · 大约1年之前

你这map和slice不是nil?这倒是让我长见识了,将空指针转为interface之后居然会被封装一层,导致nil的判断失效了。slice和map是独立的数据类型,不能只用Ptr

func IsNil(x interface{}) bool {
    if x == nil {
        return true
    }
    rv := reflect.ValueOf(x)
    switch rv.Kind() {
    case reflect.Ptr, reflect.Slice, reflect.Map, reflect.Chan, reflect.Func:
        return rv.IsNil()
    default:
        return false
    }
}
TimLiuDream
TimLiuDream · #3 · 大约1年之前
GahdWuGahdWu #2 回复

你这map和slice不是nil?这倒是让我长见识了,将空指针转为interface之后居然会被封装一层,导致nil的判断失效了。slice和map是独立的数据类型,不能只用Ptr ```go func IsNil(x interface{}) bool { if x == nil { return true } rv := reflect.ValueOf(x) switch rv.Kind() { case reflect.Ptr, reflect.Slice, reflect.Map, reflect.Chan, reflect.Func: return rv.IsNil() default: return false } } ```

是的,你这个是没有毛病的。

news001
news001 · #4 · 大约1年之前
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传