美文网首页
错误处理

错误处理

作者: 酷走天涯 | 来源:发表于2018-12-25 09:24 被阅读15次
    • 什么是错误?
    • 错误的表示
    • 获取错误详细信息的各种方法
    • 不能忽视错误

    先看一个例子

    package main
    
    import (
        "fmt"
        "os"
    )
    
    func main() {
        f, err := os.Open("/test.txt")
        if err != nil {
            fmt.Println(err)
            return
        }
        fmt.Println(f.Name(), "opened successfully")
    }
    
    image.png

    打开文件的函数原型如下

    func Open(name string) (file *File, err error)
    

    当我试图打开一个不存在的文件时,err 返回一个不为nil的值,如果文件存在err就会返回nil

    那么 error 到底是什么?

    type error interface {  
        Error() string
    }
    

    那么我们就知道了 error 就是就是一个抽象的接口,那么open 方法中的error 对应的底层结构体是什么


    断言底层类型,获取更多属性

    type PathError struct {  
        Op   string
        Path string
        Err  error
    }
    
    func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
    

    所以我们可以将error 类型转换成 pathError 然后查看里面的具体信息

    package main
    
    import (  
        "fmt"
        "os"
    )
    
    func main() {  
        f, err := os.Open("/test.txt")
        if err, ok := err.(*os.PathError); ok {
            fmt.Println("File at path", err.Path, "failed to open")
            return
        }
        fmt.Println(f.Name(), "opened successfully")
    }
    

    断言底层类型,调用方法获取更多信息

    比如请求网络,发生错误,那是由于网路超时 还是 其他原因导致的失败,我们怎么知道呢?

    type DNSError struct {  
        ...
    }
    
    func (e *DNSError) Error() string {  
        ...
    }
    func (e *DNSError) Timeout() bool {  
        ... 
    }
    func (e *DNSError) Temporary() bool {  
        ... 
    }
    

    看下面代码

    package main
    
    import (
        "fmt"
        "net"
    )
    
    func main() {
        addr, err := net.LookupHost("niubi345.com")
        if err, ok := err.(*net.DNSError); ok {
            if err.Timeout() {
                fmt.Println("operation timed out")
            } else if err.Temporary() {
                fmt.Println("temporary error")
            } else {
                fmt.Println("generic error: ", err)
            }
            return
        }
        fmt.Println(addr)
    }
    
    image.png

    这样我们就可根据不同的方法返回值来进一步判断错误类型了

    直接比较

    第三种获取错误的更多信息的方式,是与 error 类型的变量直接比较

    看一个例子

    package main
    
    import (  
        "fmt"
        "path/filepath"
    )
    
    func main() {  
        files, error := filepath.Glob("[")
        if error != nil && error == filepath.ErrBadPattern {
            fmt.Println(error)
            return
        }
        fmt.Println("matched files", files)
    }
    

    filepath.ErrBadPattern 是filepath 包里面的一个公开变量 如下

    var ErrBadPattern = errors.New("syntax error in pattern")
    

    errors 是什么?

    image.png

    errors 包中通过new 创建了一个结构体,这个结构体实现了错误接口Error()

    不可忽略错误

    package main
    
    import (  
        "fmt"
        "path/filepath"
    )
    
    func main() {  
        files, _ := filepath.Glob("[")
        fmt.Println("matched files", files)
    }
    

    我们已经从前面的示例知道了这个模式是错误的。在第 9 行,通过使用 _ 空白标识符,我忽略了 Glob 函数返回的错误。我在第 10 行简单打印了所有匹配的文件。该程序会输出


    自定义error 错误

    // Package errors implements functions to manipulate errors.
    package errors
    
    // New returns an error that formats as the given text.
    func New(text string) error {
        return &errorString{text}
    }
    
    // errorString is a trivial implementation of error.
    type errorString struct {
        s string
    }
    
    func (e *errorString) Error() string {
        return e.s
    }
    

    *fmt.Errorf 可以携带更多错误信息

    package main
    
    import (  
        "fmt"
        "math"
    )
    
    func circleArea(radius float64) (float64, error) {  
        if radius < 0 {
            return 0, fmt.Errorf("Area calculation failed, radius %0.2f is less than zero", radius)
        }
        return math.Pi * radius * radius, nil
    }
    
    func main() {  
        radius := -20.0
        area, err := circleArea(radius)
        if err != nil {
            fmt.Println(err)
            return
        }
        fmt.Printf("Area of circle %0.2f", area)
    }
    

    使用结构体类型和字段提供错误的更多信息

    定义错类类型结构体

    type areaError struct {  
        err    string
        radius float64
    }
    
    func (e *areaError) Error() string {  
        return fmt.Sprintf("radius %0.2f: %s", e.radius, e.err)
    }
    

    注意 我们是在areaError 实现的方法,所以areaError 是实现了接口,但是areaError 没有实现 所以我们使用的时候,一定要传递*areaError

    如下

    func circleArea(radius float64) (float64, error) {
      if radius < 0 {
         return 0, &areaError{"radius is negative", radius}
      }
         return math.Pi * radius * radius, nil
    }
    

    解析错误 需要进行数据转换

    radius := -20.0
       area, err := circleArea(radius)
      if err != nil {
      if err, ok := err.(*areaError); ok {
       fmt.Printf("Radius %0.2f is less than zero", err.radius)
       return
      }
    

    让错误类型携带更多的信息

    type areaError struct {  
        err    string //error description
        length float64 //length which caused the error
        width  float64 //width which caused the error
    }
    func (e *areaError) Error() string {  
        return e.err
    }
    
    func (e *areaError) lengthNegative() bool {  
        return e.length < 0
    }
    
    func (e *areaError) widthNegative() bool {  
        return e.width < 0
    }
    

    相关文章

      网友评论

          本文标题:错误处理

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