错误处理

  • 什么是错误?
  • 错误的表示
  • 获取错误详细信息的各种方法
  • 不能忽视错误

先看一个例子

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
}

你可能感兴趣的:(错误处理)