> 关注公众号【爱发白日梦的后端】分享技术干货、读书笔记、开源项目、实战经验、高效开发工具等,您的关注将是我的更新动力!
## 背景
很久之前发过一篇文章:《[10个令人惊叹的Go语言技巧,让你的代码更加优雅](https://mp.weixin.qq.com/s/VOrh6zYR1C25sZgCj2lWvw)》,这篇文章中第八点有一处错误的地方被认真的读者发现了:
![Snipaste_2024-01-15_00-22-06.png](https://static.golangjob.cn/240115/6965139d87032259f89d66aa43220b8a.png)
于是我有空之后,立马重新看了那篇文章的内容,确实是存在读者所说的问题。
## 问题
问题就在于下面这句话,文章也是有列出的:
> 即使接口持有的值为 `nil`,也不意味着接口本身为 `nil`。
>
但是在执行以下语句的时候,是有可能报 `panic` 的:
```go
return reflect.ValueOf(x).IsNil()
```
而输出也是非常明显的指出错误:
```go
panic: reflect: call of reflect.Value.IsNil on int Value
```
因为不可 `nil` 的 `interface` 是不能使用 `reflect.Value.IsNil` 方法。
那么问题就很好解决了。
## 解决方式
我们在执行 `reflect.Value.IsNil` 方法之前,进行一次判断是否为指针即可:
```go
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`。
下面我们使用几种常见的数据类型来进行测试:
```go
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`、`*int`、`slice`、`map`、`interface{}`、`自定义结构体` 来测试此 `IsNil` 方法。运行程序输出为:
```go
int IsNil: false
intPtr IsNil: true
slice IsNil: false
map IsNil: false
interface IsNil: true
structPtr IsNil: true
```
从测试结果来看,目前是符合我们对此方法的定位的。
```go
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
```
#1
更多评论
你这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
}
}
```
#2