Go 的 nil 值判断,千方百计,还是踩坑!

news/2024/11/29 6:52:23/

坚持思考,就会很酷

401418f731c606dd94318faff5311253.png

今天奇伢给大家分享一个实际踩坑的一个示例,以为的 nil 并不是 nil。众所周知,Go 编程中 nil 值的判断可谓是随处可见:

v := findSomething()
if v != nil {// do something
}

nil 值究竟是什么?这个在之前的文章也分析过( 深度剖析 Go 的 nil ),并且也提到了在 interface 相关赋值的时候有差异。但是在实际的项目开发的时候,由于流程过于复杂,代码量过于多,还是不可避免的踩到坑。什么坑呢?

以为某个 interface 判断应该是 nil ,但其实判断是 非 nil 的。其实这个知识点以前也理解过,但是又踩到了,太隐蔽了,所以专门分享一次。

d9073b61958a48d67b711c5b63cb9dc8.png

复习 interface 的 nil 知识

ae1285a792ce25b2efc1c32d362dfc63.png

它的定义长这样:

type iface struct {tab *itabdata unsafe.Pointer
}
type eface struct {_type *_typedata unsafe.Pointer
}
  • interface 变量定义是一个 16 个字节的结构体,首 8 字节是类型字段,后 8 字节是数据指针。普通的 interface 是 iface 结构,interface{} 对应的是 eface 结构;

  • interface 变量新创建的时候是 nil ,则这 16 个字节是全 0 值;

  • interface 变量的 nil 判断,汇编逻辑是判断首 8 字节是否是 0 值;

2e9531e15643f4c6e853ca0a8f5eb2c2.png

踩坑记录

8575309186f179dbffe5632ce2b0ee1d.png

奇伢的踩坑操作很简单,但也还是把我下了一身冷汗。在一个函数里明明返回了 nil 值,但是到了外面判断却是非 nil

type Worker interface {Work() error
}type Qstruct struct{}func (q *Qstruct) Work() error {return nil
}// 返回一个 nil 
func findSomething() *Qstruct {return nil
}func main() {// 定义好接口var v Workerv = findSomething()if v != nil {// 走的是这个分支fmt.Printf("v(%v) != nil\n", v)} else {fmt.Printf("v(%v) == nil\n", v)}
}

震惊!findSomething 这个函数返回的明确是 nil,但是 if 判断的分支走的是 != nil 这个分支。当时我确实愣了 10 秒,然后才反应过来。虽然知识点以前知道,但是实际编程还是不免踩坑。

 1   深究下这里的原因是啥?

回到 v = findSomething() 这行代码。这个是关键。这个是一个赋值操作,左边是一个接口变量,函数 findSomething 返回的是一个具体类型指针。所以,它一定会把接口变量 iface 前 8 字节设置非零字段的,因为有具体类型呀(无论具体类型是否是 nil 指针)。而判断 interface 是否是 nil 值,则是只根据 iface 的前 8 字节是否是零值判断的。

划重点:具体类型到接口的赋值一定会导致接口非零(不考虑编译不过等问题)。

这个问题的原理就这样简单。

 2   那么怎么改呢?

记住一个原则:如果任何地方有判断接口是否为 nil 值的逻辑,那我建议你一定不要写任何有 接口 = 具体类型(nil) 逻辑的代码。如果是 nil 值就直接赋给接口,而不要过具体类型的转换。

所以上面的改动很简单:

// 如果 findSomething 需要返回 nil 值,那么直接返回 nil 的 interface 
func findSomething() Worker {return nil
}

这样,findSomething 需要返回 nil 的时候,则是直接返回 nil 的 interface,这是一个 16 个字节全零的变量。而在外面赋值给 v 的时候,则是 interface 到 interface 的赋值,所以 v = findSomething() 的赋值之后,v 还是全 0 值。

53145f78c5a2947bd82e163adcd0681e.png

总结

3b76715f41c297ade4556169b6ea042d.png

  1. 千万要注意。如果 interface 被具体类型变量赋值过,变量的类型会被赋值到首 8 字节。从而导致 interface 非 nil 。无论具体类型变量本身是不是 nil;

  2. 如果函数返回具体类型,然后在其他地方又要赋值给接口,那还不如函数直接返回接口类型;

  3. 过一遍 interface 的原理,背诵:千万不要写任何可能存在 接口 = 具体类型(nil) 的代码,如果有 nil 值,直接赋给接口吧,不要再过中间商了;

  4. 再背一遍;

155a6b93fa7e708fee0d319e91cec77c.png

后记

8535b49825062e31c42c6507576d9ba4.png

点赞、在看 是对奇伢最大的支持。

~完~

7b0e2f457c362a5e26ae106e17375263.png

往期推荐

3f26c8444dcc542ec2d8e702a78057a6.png

往期推荐

深度细节 | Go 的 panic 的三种诞生方式

深度细节 | Go 的 panic 的秘密都在这

深度剖析 Go 的 nil

坚持思考,方向比努力更重要。关注我:奇伢云存储。欢迎加我好友,技术交流。


http://www.ppmy.cn/news/259472.html

相关文章

量子有什么特性计算机,量子是什么、量子具有什么特性、又有什么作用?

量子究竟是什么 量子(quantum)是现代物理的重要概念。即一个物理量如果存在最小的不可分割的基本单位,则这个物理量是量子化的,并把最小单位称为量子。 1900 年,普朗克首次提出量子概念,用来解决困惑物理界的“紫外灾难”问题。 紫外灾难:19世纪末,科学界许多科学家已经开…

人民日报谈焦虑成社会常见病:好的人生需要文火慢炖

现在不少人感觉到,焦虑、着急和不耐烦仿佛成了一些人的常见病,干什么事都显得急不可耐,总是等不得、坐不住、慢不了和静不下。 比如,有的人看到他人成名成功了,一下子会乱了自己的方寸、节奏和步伐,变得焦…

计算机的设计模式是什么意思,到底什么是设计模式

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 什么是模式,这是一个看上去简单,但是又非常不简单的问题。我见过很多人,学习模式理论已经很长时间了,但是并不真正理解这一点。 模式理论的基本思想其实起源于中国,是中国…

什么高大填空四个字动人_照样子填空填四字成语什么什么什么地想

照样子填空填四字成语什么什么什么地想以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 照样子填空填四字成语什么什么什么地想 绞尽脑汁jiǎo jn nǎo zhī [释义] 绞:挤&…

AI绘画程序

当前热门小程序:AI绘画 变脸 转漫画,视频的AI换脸,都是最近比较热门的,多数跟AI绘画变脸的素材播放率都挺高的。 那么这些点赞率高的作品也是通过一些AI绘画程序来制作。一般的程序可以部分免费,多数是需要开通会员亦…

【软件测试】单元测试、系统测试、集成测试的区别及示例

目录 一、单元测试 二、集成测试 三、系统测试 一、单元测试 定义:单元测试是对软件组成单元进行测试(细粒度)测试目的:用于检验软件基本组成单位的正确性测试对象:一个工作单元,通常是类内部的一个方法…

为什么性能测试PR单机版要升级到P-One一站式性能测试平台

目前市面上常用的性能测试工具均为单机版,导致了一些问题: 1.脚本无法进行共享; 2.执行管理无法进行统一化、协同化管理; 3.项目经理只能通过日报或者线下沟通获取当前项目进度; 4.对测试结果只能通过文件传输的方式进…

【论文阅读】Twin Neural Network Regression

论文下载 GitHub bib: ARTICLE{SebastianKevin2022Twin,title {Twin neural network regression},author {Sebastian Johann Wetzel and Kevin Ryczko and Roger Gordon Melko and Isaac Tamblyn},journal {Applied AI Letters},year {2022},volume {3},number …