Go“一个包含nil指针的接口不是nil接口”踩坑

简介: Go“一个包含nil指针的接口不是nil接口”踩坑最近在项目中踩了一个深坑——“Golang中一个包含nil指针的接口不是nil接口”,总结下分享出来,如果你不是很理解这句话,那推荐认真看下下面的示例代码,避免以后写代码时踩坑。

Go“一个包含nil指针的接口不是nil接口”踩坑
最近在项目中踩了一个深坑——“Golang中一个包含nil指针的接口不是nil接口”,总结下分享出来,如果你不是很理解这句话,那推荐认真看下下面的示例代码,避免以后写代码时踩坑。

示例一
先一起来看下这段代码,你感觉有没有问题呢?

type IPeople interface {

hello()

}
type People struct {
}

func (p *People) hello() {

fmt.Println("github.com/meetbetter")

}

func errFunc1(in int) *People {

if in == 0 {
    fmt.Println("importantFunc返回了一个nil")
    return nil
} else {
    fmt.Println("importantFunc返回了一个非nil值")
    return &People{}
}

}

func main() {

var i IPeople

in := 0

i = errFunc1(in)

if i == nil {

    fmt.Println("哈,外部接收到也是nil")
} else {

    fmt.Println("咦,外部接收到不是nil哦")
    fmt.Printf("%v, %T\n", i, i)
}

}
这段代码的执行结果是:

importantFunc返回了一个nil
咦,外部接收到不是nil哦
, *main.People
可以看到在main函数中收到的返回值不是nil, 明明在errFunc1()函数中返回的是nil,到了main函数为什么收到的不是nil呢?
这是因为:将nil赋值给People后再将People赋值给interface,People本身是是个指向nil的指针,但是将其赋给接口时只是接口中的值为nil,但是接口中的类型信息为main.People而不是nil,所以这个接口不是nil。
是的,Golang中的interface类型包含两部分信息——值信息和类型信息,只有interface的值合并类型都为nil时interface才为nil,interface底层实现可以在后面的源码分析看到。

先来看看正确的处理接口返回值的方法,是直接将nil赋给interface:

func rightFunc(in int) IPeople {

if in == 0 {
    fmt.Println("importantFunc返回了一个nil")
    return nil
} else {
    fmt.Println("importantFunc返回了一个非nil值")
    return &People{}
}

}
示例二
下面的代码更清晰的证明了一个包含nil指针的接口不是nil接口的结论:

type IPeople interface {

hello()

}
type People struct {
}

func (p *People) hello() {

fmt.Println("github.com/meetbetter")

}

//错误:将nil的people给空接口后接口就不为nil,因为interface中的value为nil但type不为nil
func errFunc() *People {

var p *People

return p

}

//正确处理返回nil给接口的方式:直接将nil赋给interface
func rightFunc() IPeople {

var p *People

return p

}
func main() {

if errFunc() == nil {

    fmt.Println("对了哦,外部接收到也是nil")
} else {

    fmt.Println("错了咦,外部接收到不是nil哦")

}

if rightFunc() == nil {

    fmt.Println("对了哦,外部接收到也是nil")
} else {

    fmt.Println("错了咦,外部接收到不是nil哦")

}

}
输出结果:

对了哦,外部接收到也是nil
错了咦,外部接收到不是nil哦
interface底层实现
下面的注释信息来自参考文章中,从interface底层实现可以看出iface比eface 中间多了一层itab结构, itab 存储_type信息和[]fun方法集,所以即使data指向了nil 并不代表interface 就是nil, 还要考虑_type信息。

type eface struct { //空接口

_type *_type         //类型信息
data  unsafe.Pointer //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer类似于c语言中的void*)

}
type iface struct { //带有方法的接口

tab  *itab           //存储type信息还有结构实现方法的集合
data unsafe.Pointer  //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer类似于c语言中的void*)

}
type _type struct {

size       uintptr  //类型大小
ptrdata    uintptr  //前缀持有所有指针的内存大小
hash       uint32   //数据hash值
tflag      tflag
align      uint8    //对齐
fieldalign uint8    //嵌入结构体时的对齐
kind       uint8    //kind 有些枚举值kind等于0是无效的
alg        *typeAlg //函数指针数组,类型实现的所有方法
gcdata    *byte
str       nameOff
ptrToThis typeOff

}
type itab struct {

inter  *interfacetype  //接口类型
_type  *_type          //结构类型
link   *itab
bad    int32
inhash int32
fun    [1]uintptr      //可变大小 方法集合

}
以上完整代码均整理在Github-跟着示例代码学Golang项目。

参考文章:
Golang第一大坑

"一个包含nil指针的接口不是nil接口"的讨论
原文地址https://www.cnblogs.com/CodeWithTxT/p/11297300.html

相关文章
|
9月前
|
存储 Go
Go语言之接口与多态 -《Go语言实战指南》
Go 语言中的接口是实现多态的核心机制,通过一组方法签名定义行为。任何类型只要实现接口的所有方法即视为实现该接口,无需显式声明。本文从接口定义、使用、底层机制、组合、动态行为到工厂模式全面解析其特性与应用,帮助理解 Go 的面向接口编程思想及注意事项(如 `nil` 陷阱)。
249 22
|
4月前
|
Java 编译器 Go
【Golang】(5)Go基础的进阶知识!带你认识迭代器与类型以及声明并使用接口与泛型!
好烦好烦好烦!你是否还在为弄不懂Go中的泛型和接口而烦恼?是否还在苦恼思考迭代器的运行方式和意义?本篇文章将带你了解Go的接口与泛型,还有迭代器的使用,附送类型断言的解释
245 3
|
4月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
278 1
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
9月前
|
存储 JSON Go
Go语言之空接口与类型断言
本文介绍了 Go 语言中空接口(`interface{}`)和类型断言的核心概念及其应用。空接口可存储任意类型数据,适用于通用函数、动态数据结构与 JSON 解析等场景;类型断言用于将接口变量还原为具体类型,推荐使用带 `ok` 的写法以避免程序崩溃。此外,文章通过示例讲解了 `type switch` 类型判断与 JSON 处理技巧,并总结了空接口的注意事项,强调滥用可能导致类型安全性降低。内容深入浅出,帮助开发者灵活运用这些特性。
270 15
|
9月前
|
Go
Go语言接口的定义与实现
Go 语言的接口提供了一种灵活的多态机制,支持隐式实现和抽象编程。本文介绍了接口的基本定义、实现方式、空接口的使用、类型断言以及接口组合等核心概念,并探讨了接口与 nil 的关系及应用场景。通过示例代码详细说明了如何利用接口提升代码的可扩展性和可测试性,总结了接口的关键特性及其在依赖注入、规范定义和多态调用中的重要作用。
363 14
|
存储 Rust Go
Go nil 空结构体 空接口有什么区别?
本文介绍了Go语言中的`nil`、空结构体和空接口的区别。`nil`是预定义的零值变量,适用于指针、管道等类型;空结构体大小为0,多个空结构体实例指向同一地址;空接口由`_type`和`data`字段组成,仅当两者均为`nil`时,空接口才为`nil`。
289 1
Go nil 空结构体 空接口有什么区别?
|
存储 Go
Go to Learn Go之接口
Go to Learn Go之接口
157 7
|
存储 安全 程序员
|
存储 Go
Go to Learn Go之指针
Go to Learn Go之指针
145 0