Golang接口值(interface)的比较操作分析

作者: veaxen 分类: Golang 发布时间: 2018-11-29 22:39

Golang的interface设计得相当巧妙,作为一名菜鸟,在学习的时候就遇到了一些疑问,比如关于interface类型值的逻辑比较(等于==,不等于!=)。在《Go语言圣经》第7章5小节中提到:两个接口值相等仅当它们都是nil值或者它们的动态类型相同并且动态值也根据这个动态类型的==操作相等。

这句话比较拗口,我们得好好理解理解,一般难读的文字意义都不简单(坑都比较深),为了成为更好的程序员,我决定咬文嚼字一波。

interface接口值的原理

在Go语言中,interface的实际实现结构可以理解为下图:

blob.jpg

其中type就是它的类型(动态类型),value部分是它的值(动态值)。

一个interface类型的变量 w 为nil,就代表着其动态类型和动态值都为 nil 。考虑这种情况:

blob.jpg

图中代表着类型不为空,但是interface的动态值是 nil ,那么这种情况下,如果去判断 w 是否为 nil 时,会得到一个false

fmt.Println(w == nil)  //输出 false

要特别注意这钟情况,因为我们可能会犯这样的错误:

if w != nil {
    w.Write([]byte("done!\n")) // 当w的动态值为nil时,会发生panic
}

动态类型为指针的interface

当interface的动态类型是指针的时候,且其动态值不为 nil 时,我们可以理解为其结构如下图所示。

blob.jpg

这里为什么需要特别拿出来说明呢?因为动态类型为指针的interface的动态值保存的就是一个指针值,这个指针指向一块内存。这感觉好像没啥呀?再想想我们开头说的那句很拗口的话。下面以Golang的error接口来说明这个问题。

下面是error包的代码:

type error interface {
    Error() string
}

func New(text string) error { return &errorString{text} }

type errorString struct { text string }

func (e *errorString) Error() string { return e.text }

这里需要理解的:是指针类型 *errorString 实现了error接口,而不是 errorString 。

w1 := errors.New("ERR")
w2 := errors.New("ERR")
fmt.Println(w1 == w2) // 输出false

以 w1 为例子, 由于是指针类型 *errorString 实现了error接口,所以 w1 的动态类型是 *errorString,那么 w1 的动态值就是一个指针,w2 也是同理。那么上面的等于(==)比较我们可以用下图和伪代码来理解。

blob.jpg

w1.type == w2.type && w1.value == w2.value

由于 w1.value 和 w2.value 都是指针类型,它们又分别保存着不同的内存地址,所以他们的比较是得出 false

也正是这种实现,每个New函数的调用都分配了一个独特的和其他错误不相同的实例,这能方便的让我们可以定义自己特定的错误,就如同Golang定义的 io.EOF 一样,不必担心刚好有相同的错误消息:

fmt.Println(errors.New("EOF") == io.EOF)  //输出false

参考:《Go语言圣经》

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据