【开发掉坑】go 中 interface 的 nil 判断

2024-01-20 00:20
文章标签 go 开发 判断 nil interface

本文主要是介绍【开发掉坑】go 中 interface 的 nil 判断,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

今天介绍下 go 中的 interface(any)nil 判断,项目中遇到的一个小问题,知识遗忘了,再做个记录。

前言

最近在合作开发项目的过程中,发现小伙伴写了一段代码,示意代码如下:

package mainimport("encoding/json""fmt"
)type dataWrapper struct {data any
}func convert(v any) *dataWrapper {d := new(dataWrapper)d.data = vreturn d
}type sureData struct {Name string
}func (d *dataWrapper) sureData() *sureData {buf, _ := json.Marshal(d.data)data := new(sureData)json.Unmarshal(buf, data)return data
}func main() {var data *sureDatafmt.Println("is nil: ", data == nil) // truesd := convert(data).sureData()fmt.Println("is nil: ", sd == nil) // falseif sd == nil {// 逻辑代码} else {// 逻辑代码}
}

输出:

is nil:  true
is nil:  false

由于该代码仓库是为了让其他项目使用,基于之前的老项目抽离出来的,老项目的结构体和新项目不同,但是字段都是一样的,要进行结构体转换,偷懒用 jsonMarshalUnmarshal 来做的(这种方式不认同,对调用方不友好,而且效率还差)。

这里的 dataWrapper 就是进行结构体转换的一个封装,最终使用 sureData 方法获取真正的结构体数据。

代码简单,可以看到这里的 sureData 方法获取的数据肯定不为空,因为它在方法里做了 new(sureData) 了,返回的结构体肯定不为空。

代码看到这里,想要使其能正确地判断 nil,对 sureData 方法进行了如下修改(当然只是做示例用,真实场景中不推荐):

func (d *dataWrapper) sureData() *sureData {if d.data == nil {return nil}buf, _ := json.Marshal(d.data)data := new(sureData)json.Unmarshal(buf, data)return data
}

但是运行查看输出结果和刚刚没区别

is nil:  true
is nil:  false

为什么传给 dataWrppernil 值再判断就不为 nil 了呢?按理说 sureData 得到的值应该是 nil 才对,这就引出了今天主题,判断 interface(any) 是否为 nil

原理解析

先看下 interface 的底层结构,分为两种:包含 methodiface 和不包含 methodeface,也就是 empty interface

本篇介绍基于 go1.20 版本,源码在:src/runtime/runtime2.go,具体结构如下

type iface struct {tab  *itabdata unsafe.Pointer
}type eface struct {_type *_typedata  unsafe.Pointer
}

具体的 iface, eface 结构就不在此篇介绍了。

结论:当我们判断一个 interface 的值是否为 nil 时,需要这个值的动态类型和动态值都为 nil 时,这个 interface 才会是 nil

可以看以下例子来加深印象:

package mainfunc main() {var a any = nilvar b any = (*string)(nil)println(a==nil) // trueprintln(b==nil) // false
}

解决方案

  1. 反射

注意查看 nil 的定义(源码:src/builtin/builtin.go),使用反射进行 nil 判断时需要注意类型只能是 pointer, channel, func, interface, map, or slice type,使用其他类型会直接 panic

// nil is a predeclared identifier representing the zero value for a
// pointer, channel, func, interface, map, or slice type.
var nil Type // Type must be a pointer, channel, func, interface, map, or slice type
func IsNil(v any) bool {valueOf := reflect.ValueOf(v)k := valueOf.Kind()switch k {case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:return valueOf.IsNil()default:return v == nil}
}
  1. interface 底层结构

可以模拟 eface 的结构来进行 nil 判断,不建议这么使用,还是用 reflect 官方包比较好。

type eface struct {rtype unsafe.Pointerdata  unsafe.Pointer
}func IsNil(obj any) bool {return (*eface)(unsafe.Pointer(&obj)).data == nil
}
  1. 明确知道 interface 类型的值

可以使用类型断言,建议使用。

type Dog struct{}
type Cat struct{}
func IsNil(obj any) bool {switch obj.(type) {case *Dog:return obj.(*Dog) == nilcase *Cat:return obj.(*Cat) == nil}return obj == nil
}

总结

本篇以实际开发过程中的一个例子作为引导,介绍了 go 中的 interfacenil 判断的坑点。

解释了其原理:只有当类型和值都为空时,接口才为空。

参考

  • golang interface判断为空nil
  • Golang interface的类型断言是如何实现

这篇关于【开发掉坑】go 中 interface 的 nil 判断的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/624216

相关文章

go中的时间处理过程

《go中的时间处理过程》:本文主要介绍go中的时间处理过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 获取当前时间2 获取当前时间戳3 获取当前时间的字符串格式4 相互转化4.1 时间戳转时间字符串 (int64 > string)4.2 时间字符串转时间

Go语言中make和new的区别及说明

《Go语言中make和new的区别及说明》:本文主要介绍Go语言中make和new的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 概述2 new 函数2.1 功能2.2 语法2.3 初始化案例3 make 函数3.1 功能3.2 语法3.3 初始化

Go语言中nil判断的注意事项(最新推荐)

《Go语言中nil判断的注意事项(最新推荐)》本文给大家介绍Go语言中nil判断的注意事项,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1.接口变量的特殊行为2.nil的合法类型3.nil值的实用行为4.自定义类型与nil5.反射判断nil6.函数返回的

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

SpringBoot开发中十大常见陷阱深度解析与避坑指南

《SpringBoot开发中十大常见陷阱深度解析与避坑指南》在SpringBoot的开发过程中,即使是经验丰富的开发者也难免会遇到各种棘手的问题,本文将针对SpringBoot开发中十大常见的“坑... 目录引言一、配置总出错?是不是同时用了.properties和.yml?二、换个位置配置就失效?搞清楚加

Go语言代码格式化的技巧分享

《Go语言代码格式化的技巧分享》在Go语言的开发过程中,代码格式化是一个看似细微却至关重要的环节,良好的代码格式化不仅能提升代码的可读性,还能促进团队协作,减少因代码风格差异引发的问题,Go在代码格式... 目录一、Go 语言代码格式化的重要性二、Go 语言代码格式化工具:gofmt 与 go fmt(一)

Python中对FFmpeg封装开发库FFmpy详解

《Python中对FFmpeg封装开发库FFmpy详解》:本文主要介绍Python中对FFmpeg封装开发库FFmpy,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、FFmpy简介与安装1.1 FFmpy概述1.2 安装方法二、FFmpy核心类与方法2.1 FF

基于Python开发Windows屏幕控制工具

《基于Python开发Windows屏幕控制工具》在数字化办公时代,屏幕管理已成为提升工作效率和保护眼睛健康的重要环节,本文将分享一个基于Python和PySide6开发的Windows屏幕控制工具,... 目录概述功能亮点界面展示实现步骤详解1. 环境准备2. 亮度控制模块3. 息屏功能实现4. 息屏时间

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部

python判断文件是否存在常用的几种方式

《python判断文件是否存在常用的几种方式》在Python中我们在读写文件之前,首先要做的事情就是判断文件是否存在,否则很容易发生错误的情况,:本文主要介绍python判断文件是否存在常用的几种... 目录1. 使用 os.path.exists()2. 使用 os.path.isfile()3. 使用