golang err!=nil

2022-09-15  本文已影响0人  小白小白啦

今天写代码的时候,发现一个和预期不符合的逻辑。简单描述就是把一个值为nil的A类型的error赋值给接口error,和nil比较竟然是false。在此记录一下。

复现

代码如下,自定义一个error类型。然后预计代码输出是<nil> false,但是实际输出是<nil> true。非常非常奇怪,nil竟然不等于nil

package main

import "fmt"

type Err struct {
    err string
}

func (e *Err) Error() string {
    return e.err
}
func returnErr() *Err {
    return nil
}
func main() {
    var err error
    err = returnErr()
    fmt.Println(err, err != nil)
}

解决办法
不要将该结果赋给一个接口变量。如,将 err = returnErr() 改成 err1 := returnErr()

func main() {
    err := returnErr()
    fmt.Println(err, err != nil)
}

或者和原类型比较

func main() {
    var err error
    err = returnErr()
    var e *Err
    fmt.Println(err, err != e)
}

原因

接口 interface 造成的。具体可以查看 官网 FAQ

简单说,interface 被两个元素 value 和 type 所表示。只有在 value 和 type 同时为 nil 的时候,判断 interface == nil 才会为 true。而 err = returnErr() 这个过程中,虽然 value 为 nil,但 type 却为 *Err。

下面的内容从《Go语言精进之路:从新手到高手编程思想、方法和技巧1》拷贝

接口类型“动静兼备”的特性决定了它的变量的内部表示绝不像静态类型(如int、float64)变量那样简单。我们可以在$GOROOT/src/runtime/runtime2.go中找到接口类型变量在运行时的表示:

image.png
我们看到在运行时层面,接口类型变量有两种内部表示——eface和iface,这两种表示分别用于不同接口类型的变量。eface:用于表示没有方法的空接口(empty interface)类型变量,即interface{}类型的变量iface:用于表示其余拥有方法的接口(interface)类型变量。这两种结构的共同点是都有两个指针字段,并且第二个指针字段的功用相同,都指向当前赋值给该接口类型变量的动态类型变量的值。
image.png
而iface除了要存储动态类型信息之外,还要存储接口本身的信息(接口的类型信息、方法列表信息等)以及动态类型所实现的方法的信息,因此iface的第一个字段指向一个itab类型结构:
image.png
上面itab结构中的第一个字段inter指向的interfacetype结构存储着该接口类型自身的信息。interfacetype类型定义如下,该interfacetype结构由类型信息(typ)、包路径名(pkgpath)和接口方法集合切片(mhdr)组成。
itab结构中的字段_type则存储着该接口类型变量的动态类型的信息,字段fun则是动态类型已实现的接口方法的调用地址数组。

参考

Golang 博主走过的有关 error 的一些坑

上一篇下一篇

猜你喜欢

热点阅读