脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Golang - 这个 Go 语言的经典 “坑”,我算是服了

这个 Go 语言的经典 “坑”,我算是服了

2022-01-04 22:44Go编程时光写代码的明哥 Golang

在开始之前,先考你一个非常 Go 味的经典问题:如何判断一个 interface{} 的值是否为 nil ?这也是面试有可能会被问到的一个问题,这个问题很 “迷”,平时没有特别留心的朋友,很容易在这边裁了。

这个 Go 语言的经典 “坑”,我算是服了

大家好,我是明哥。

在开始之前,先考你一个非常 Go 味的经典问题:如何判断一个 interface{} 的值是否为 nil ?

这也是面试有可能会被问到的一个问题,这个问题很 “迷”,平时没有特别留心的朋友,很容易在这边裁了。

我相信很多人会下意识的回答,直接 v == nil 进行判断不就好了吗?

很久之前,我也是那么想的,可写了个 demo 后,才发现事情没那么简单。

请看下面这段代码,可以先猜测一下输出的结果。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. var a *string = nil
  7. var b interface{} = a
  8. fmt.Println(b==nil)
  9. }

答案应该会跟你下意识的回答 相反。

输出的结果的是 false

这是为什么呢?接下来,我们就要好好的唠唠这里面的道道。

1. 两个 interface 比较

interface 的内部实现包含了两个字段,一个是 type,一个是 data

对于这样一个变量

  1. var age interface{} = 25

其实内部结构是

这个 Go 语言的经典 “坑”,我算是服了

因此两个 interface 比较,势必与这两个字段有所关系。

经过验证,只有下面两种情况,两个 interface 才会相等。

第一种情况

type 和 data 都相等

在下面的代码中,p1 和 p2 的 type 都是 Profile,data 都是 {"iswbm"},因此 p1 与 p2 相等

而 p3 和 p3 虽然类型都是 *Profile,但由于 data 存储的是结构体的地址,而两个地址和不相同,因此 p3 与 p4 不相等

  1. package main
  2. import "fmt"
  3. type Profile struct {
  4. Name string
  5. }
  6. type ProfileInt interface {}
  7. func main() {
  8. var p1, p2 ProfileInt = Profile{"iswbm"}, Profile{"iswbm"}
  9. var p3, p4 ProfileInt = &Profile{"iswbm"}, &Profile{"iswbm"}
  10. fmt.Printf("p1 --> type: %T, data: %v \n", p1, p1)
  11. fmt.Printf("p2 --> type: %T, data: %v \n", p2, p2)
  12. fmt.Println(p1 == p2) // true
  13. fmt.Printf("p3 --> type: %T, data: %p \n", p3, p3)
  14. fmt.Printf("p4 --> type: %T, data: %p \n", p4, p4)
  15. fmt.Println(p3 == p4) // false
  16. }

运行后,输出如下

  1. p1 --> type: main.Profile, data: {iswbm}
  2. p2 --> type: main.Profile, data: {iswbm}
  3. true
  4. p3 --> type: *main.Profile, data: 0xc00008e200
  5. p4 --> type: *main.Profile, data: 0xc00008e210
  6. false

第二种情况

特殊情况:两个 interface 都是 nil

当一个 interface 的 type 和 data 都处于 unset 状态的时候,那么该 interface 的类型和值都为 nil

  1. package main
  2. import "fmt"
  3. func main() {
  4. var p1, p2 interface{}
  5. fmt.Println(p1 == p2) // true
  6. fmt.Println(p1 == nil) // true

2. interface 与 非 interface 比较

当 interface 与非 interface 比较时,会将 非interface 转换成 interface ,然后再按照 两个 interface 比较 的规则进行比较。

示例如下

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. func main() {
  7. var a string = "iswbm"
  8. var b interface{} = "iswbm"
  9. fmt.Println(a==b) // true
  10. }

上面这种例子可能还好理解,那么请你看下面这个例子(文章开头的示例),为什么 b 与 nil 不相等?

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. var a *string = nil
  7. var b interface{} = a
  8. fmt.Println(b==nil) // false
  9. }

但当你使用 b==nil 进行判断时,其实右边的 nil 并非单纯的是我们所理解的值为nil,而正确的理解应该是 类型为 nil 且 值也为 nil。

Go 会先将 nil 转换为interface (type=nil, data=nil) ,这与 b (type=*string, data=nil) 虽然 data 是一样的,但 type 不相等,因此他们并不相等。

那有没有办法判断一个 interface{} 是不是 nil 呢?

有办法的,但是要借助反射,一个非万不得已不会使用的 reflect 包。

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. func IsNil(i interface{}) bool {
  7. vi := reflect.ValueOf(i)
  8. if vi.Kind() == reflect.Ptr {
  9. return vi.IsNil()
  10. }
  11. return false
  12. }
  13. func main() {
  14. var a *string = nil
  15. var b interface{} = a
  16. fmt.Println(IsNil(b))
  17. }

本文通过一些例子介绍了 Go 在比较时候,内部的一些实现原理,对于很多人,可能是一个“新”知识,没有掌握的话,一定会在以后在编码过程中给自己挖了个“大坑”,而这种语言内部的 “坑”,不知道就是不知道,再怎么代码走查都很难发现。希望通过本篇文章,带你一起把这个“坑” 给填上。

本篇原属于 Go 面试题库专栏系列文章,以前都是在标题上写明了是面试题,考虑到有些人最近没有在面试,怕你们错过这类即使不面试,也要掌握的知识,以后的内容,可能不会在标题上特别标明是面试题了。

本专栏系列文章,我都公开到 Github 上:https://github.com/iswbm/golang-interview

这个号没有留言功能呢 ,如果文章有写得不对的地方,可以去那里提交 issue 帮我指正。顺便可以帮我点个小 ??,在那里我对题库进行了分类整理,方便索引查找。

加油噢,我们下篇见!

原文链接:https://mp.weixin.qq.com/s/7YX_N9ANQU06rtRaQ_-ekQ

延伸 · 阅读

精彩推荐
  • Golanggo语言制作端口扫描器

    go语言制作端口扫描器

    本文给大家分享的是使用go语言编写的TCP端口扫描器,可以选择IP范围,扫描的端口,以及多线程,有需要的小伙伴可以参考下。 ...

    脚本之家3642020-04-25
  • Golanggo日志系统logrus显示文件和行号的操作

    go日志系统logrus显示文件和行号的操作

    这篇文章主要介绍了go日志系统logrus显示文件和行号的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    SmallQinYan12302021-02-02
  • GolangGolang中Bit数组的实现方式

    Golang中Bit数组的实现方式

    这篇文章主要介绍了Golang中Bit数组的实现方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    天易独尊11682021-06-09
  • Golanggolang json.Marshal 特殊html字符被转义的解决方法

    golang json.Marshal 特殊html字符被转义的解决方法

    今天小编就为大家分享一篇golang json.Marshal 特殊html字符被转义的解决方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧 ...

    李浩的life12792020-05-27
  • Golanggolang的httpserver优雅重启方法详解

    golang的httpserver优雅重启方法详解

    这篇文章主要给大家介绍了关于golang的httpserver优雅重启的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,...

    helight2992020-05-14
  • Golanggolang如何使用struct的tag属性的详细介绍

    golang如何使用struct的tag属性的详细介绍

    这篇文章主要介绍了golang如何使用struct的tag属性的详细介绍,从例子说起,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看...

    Go语言中文网11352020-05-21
  • Golanggolang 通过ssh代理连接mysql的操作

    golang 通过ssh代理连接mysql的操作

    这篇文章主要介绍了golang 通过ssh代理连接mysql的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    a165861639710342021-03-08
  • GolangGolang通脉之数据类型详情

    Golang通脉之数据类型详情

    这篇文章主要介绍了Golang通脉之数据类型,在编程语言中标识符就是定义的具有某种意义的词,比如变量名、常量名、函数名等等,Go语言中标识符允许由...

    4272021-11-24