【开发掉坑】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语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

一文详解Python如何开发游戏

《一文详解Python如何开发游戏》Python是一种非常流行的编程语言,也可以用来开发游戏模组,:本文主要介绍Python如何开发游戏的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、python简介二、Python 开发 2D 游戏的优劣势优势缺点三、Python 开发 3D

基于Python开发Windows自动更新控制工具

《基于Python开发Windows自动更新控制工具》在当今数字化时代,操作系统更新已成为计算机维护的重要组成部分,本文介绍一款基于Python和PyQt5的Windows自动更新控制工具,有需要的可... 目录设计原理与技术实现系统架构概述数学建模工具界面完整代码实现技术深度分析多层级控制理论服务层控制注

java中判断json key是否存在的几种方法

《java中判断jsonkey是否存在的几种方法》在使用Java处理JSON数据时,如何判断某一个key是否存在?本文就来介绍三种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目http://www.chinasem.cn录第一种方法是使用 jsONObject 的 has 方法

Go语言中json操作的实现

《Go语言中json操作的实现》本文主要介绍了Go语言中的json操作的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录 一、jsOChina编程N 与 Go 类型对应关系️ 二、基本操作:编码与解码 三、结构体标签(Struc

Java中的分布式系统开发基于 Zookeeper 与 Dubbo 的应用案例解析

《Java中的分布式系统开发基于Zookeeper与Dubbo的应用案例解析》本文将通过实际案例,带你走进基于Zookeeper与Dubbo的分布式系统开发,本文通过实例代码给大家介绍的非常详... 目录Java 中的分布式系统开发基于 Zookeeper 与 Dubbo 的应用案例一、分布式系统中的挑战二

使用Go调用第三方API的方法详解

《使用Go调用第三方API的方法详解》在现代应用开发中,调用第三方API是非常常见的场景,比如获取天气预报、翻译文本、发送短信等,Go作为一门高效并发的编程语言,拥有强大的标准库和丰富的第三方库,可以... 目录引言一、准备工作二、案例1:调用天气查询 API1. 注册并获取 API Key2. 代码实现3

基于Go语言开发一个 IP 归属地查询接口工具

《基于Go语言开发一个IP归属地查询接口工具》在日常开发中,IP地址归属地查询是一个常见需求,本文将带大家使用Go语言快速开发一个IP归属地查询接口服务,有需要的小伙伴可以了解下... 目录功能目标技术栈项目结构核心代码(main.go)使用方法扩展功能总结在日常开发中,IP 地址归属地查询是一个常见需求:

基于 Cursor 开发 Spring Boot 项目详细攻略

《基于Cursor开发SpringBoot项目详细攻略》Cursor是集成GPT4、Claude3.5等LLM的VSCode类AI编程工具,支持SpringBoot项目开发全流程,涵盖环境配... 目录cursor是什么?基于 Cursor 开发 Spring Boot 项目完整指南1. 环境准备2. 创建

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版