golang中给接口类型变量赋值为nil的问题

·  阅读 46

之前遇到一个关于给接口变量赋值为nil的问题,正好借此机会再学习一下相关的知识点

1 问题

先来看下面这段代码

func getErr() error {
    return nil
}

type MyErr struct {
}

// MyErr 实现了error接口
func (m MyErr) Error() string {
    return "MyErr"
}

func (m MyErr) getErr() *MyErr {
    return nil
}

func main() {
    e := getErr()

    e = new(MyErr).getErr()

    if e != nil {
        fmt.Println("e is not nil")
    }
}


# 输出结果
e is not nil
复制代码

两处对变量e的赋值,看起来都是nil,但是在对e做判空的时候,却显示e并不是nil,不禁让人感到奇怪

2 相关知识点回顾

当给一个接口变量赋值的时候,该变量的动态类型会与它的动态值一起被存储在一个名为iface的专用数据结构中

这个iface的一个实例,才是赋给该接口变量的值

// src/runtime/runtime2.go
type iface struct {
    tab  *itab //指向类型信息的指针
    data unsafe.Pointer // 指向动态值的指针
}
复制代码

只有在以下场景中,接口变量才会是真正的nil:

  1. 声明一个接口变量但不做初始化

  2. 直接用字面量nil直接赋值

3 问题解析

我们结合知识点再回头来看上面的这段程序

这里声明了一个error接口的变量,并赋值为nil

e := getErr()
复制代码

MyErr是接口error的一个动态类型

同时,在下面这行代码中,返回的nil就是变量e的动态值

e = new(MyErr).getErr()
复制代码

所以,当上面这行代码执行完成后,e的实际值已经变成了iface类型的一个实例,而不再是nil了

因此,在我们做下面的判断时,就会进入if分支,并打印e is not nil的结果

if e != nil {
    fmt.Println("e is not nil")
}
复制代码
分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改