美文网首页
Golang 博主走过的有关 error 的一些坑

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

作者: deepzz | 来源:发表于2017-06-06 16:57 被阅读108次

    更多精彩文章https://deepzz.com
    Desc:为什么值为 nil 的 error 却不等于 nil

    本片文章用来记录我在 Golang 开发学习过程中遇到的有关 error 的一些坑。或许你也遇到,或许你能在这里找到答案。当然通过 error 的例子,你也应该联想到其它场景。

    err != nil

    第一个问题是:为什么值为 nil 的 error 却不等于 nil?

    思考如下代码:

    package main
    
    import (
        "errors"
        "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)
    }
    

    首先 returnErr() 返回了一个值为 nil 的 *Err,然后赋值给了 err,那么 fmt 会打印什么结果?是不是:

    <nil> false
    

    错,它会打印 <nil> true。当时自己也是没有头绪,学艺不精,问过大神才知道,这是接口 interface 造成的。具体可以查看 官网 FAQ

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

    如何解决这个问题呢?在我看来只能从根源上着手,两种方式:

    1. 不要将该结果赋给一个接口变量。如,将 err = returnErr()
      改成 err1 := returnErr(),这样就可以避免造成的干扰。
    2. 不要让函数返回自定义类型,如,*Err。应该将错误包装过滤为 error,如:
    func Err() (err error) {
        if e := returnErr(); e != nil {
            return e
        }
    
        return
    }
    

    err == nil

    为什么还有 err == nil的问题呢?这个问题是我看查看标准库源码的时候发现的,其中有这么一段:

    func (e *AddrError) Error() string {
        if e == nil { // 请注意,为什么这里会判断是否为 nil
            return "<nil>"
        }
        s := e.Err
        if e.Addr != "" {
            s = "address " + e.Addr + ": " + s
        }
        return s
    }
    

    为什么会再次去判断 e == nil,当时很是费解。众所周知,方法(姑且这样说)和变量是存储在不同区域的,当我们用一个空指针类型的变量(如,var e *AddrError)调用方法时,该方法是会执行的,只有在执行该空指针变量的解指针操作时,才会 panic。

    下面是一个例子:

    // Package main provides ...
    package main
    
    import "fmt"
    
    func main() {
        var e *Err
        e.Print()
        e.Printf()
        e.Println()
    }
    
    type Err struct {
        err string
    }
    
    func (e *Err) Print() {
        fmt.Println("e.Print run")
    }
    
    func (e *Err) Printf() {
        fmt.Println(e.err)
    }
    
    func (e Err) Println() {
        fmt.Println("e.Println run")
    }
    

    1、e.Print() 是完全可以执行的,没有解指针操作。

    2、e.Printf() 在调用 e.err 的时候,发生了解指针操作,故会 panic。

    3、e.Println() 由于接受者是 Err 而不是 *Err,Golang 内部在调用该函数时,会自动解指针,故会 panic。

    相关文章

      网友评论

          本文标题:Golang 博主走过的有关 error 的一些坑

          本文链接:https://www.haomeiwen.com/subject/upqnfxtx.html