error处理

go1.13之前的错误处理

错误检查
if err != nil {
    //something wrong...
}

有时我们将错误与已知的前哨值(sentinel value)进行比较来查看是否发生了特定错误。

var permissionError = errors.New("permission deny")

if err == permissionError {
    //permission deny
}

错误值可以是满足语言定义的error接口的任何类型。
程序可以使用类型断言来判断错误值是否可被视为特定的错误类型。

type NotFoundError struct {
    Name string
}

func (err *NotFoundError) Error() string {
    return err.Name + ":not found"
}

if e, ok := err.(*NotFoundError); ok {
    //e.Name wasn't found
}
添加信息

一种简单的方法是构造一个新错误,并在其中包括上一个错误。

if err != nil {
    return fmt.Errorf("decompress %v: %v", name, err)
}

使用fmt.Errorf创建的新错误将丢弃原始错误中的所有内容(文本除外)。

package main

import "fmt"

type NotFoundError struct {
    Name string
}

func (err *NotFoundError) Error() string {
    return err.Name + ":not found"
}

func main() {
    err1 := &NotFoundError{"user"}
    //err1已经变成了string,user:not found,原始错误中的所有内容都丢失了
    err2 := fmt.Errorf("db err:%v", err1)
    //db err:user:not found
    fmt.Println(err2)
}

有时我们可能想要定义一个包含基础错误的新错误类型,并将其保存下来以供代码检查。

type QueryError struct {
    Query string
    Err   error
}

func (e *QueryError) Error() string {
    return e.Query + ": " + e.Err.Error()
}

程序可以查看一个*QueryError值的内部以根据潜在的错误进行决策。

var ErrNotFound = errors.New("not found")

if e, ok := err.(*QueryError); ok && e.Err == ErrNotFound {
    // query failed because of a not found error
}

go1.13之后的错误处理

一个约定

在go 1.13中,最重要的不是改变,而是一个约定:
包含另一个错误的错误可以实现Unwrap方法来返回所包含的底层错误。
如果e1.Unwrap()返回了e2,那么我们说e1包装了e2,您可以Unwrap e1来得到e2。

用%w包装错误

在go 1.13中,fmt.Errorf函数支持新的%w动词。
当存在该动词时,被封装的原始错误不会丢失,可用于errors.Is和errors.As。

封装与Is、As
package main

import (
    "fmt"
    "github.com/pkg/errors"
)

type NotFound struct {
    Name string
}

func (err *NotFound) Error() string {
    return err.Name + " not found"
}

type RepoError struct {
    Err error
}

func (err *RepoError) Error() string {
    return "repo error:" + err.Err.Error()
}

func (err *RepoError) Unwrap() error {
    return err.Err
}

func main() {

    userNotFound := &NotFound{"user"}
    userRepoError := &RepoError{userNotFound}
    middlewareErr := fmt.Errorf("middleware:%w", userRepoError)

    shopNotFound := &NotFound{"shop"}

    //Is reports whether any error in err's chain matches target.
    fmt.Println(errors.Is(middlewareErr, userNotFound))  //true
    fmt.Println(errors.Is(middlewareErr, userRepoError)) //true
    fmt.Println(errors.Is(middlewareErr, shopNotFound))  //false,errors.Is函数将错误与值进行比较

    userNotFoundTarget := &NotFound{}
    userRepoErrorTarget := &RepoError{}

    //As finds the first error in err's chain that matches target, and if one is found, sets target to that error value and returns true.
    fmt.Println(errors.As(middlewareErr, &userNotFoundTarget)) //true,As函数用于测试错误是否为特定类型
    fmt.Println(userNotFoundTarget)                            //user not found

    fmt.Println(errors.As(middlewareErr, &userRepoErrorTarget)) //true
    fmt.Println(userRepoErrorTarget)                            //repo error:user not found

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 在Go中,对于处理错误一般分为两种情况: 错误和异常. 在Go中,错误的处理一般都是通过 error接口来指定;异...
    张哈阅读 1,776评论 0 2
  • 背景介绍 如果你有写过Go代码,那么你可以会遇到Go中内建类型error。Go语言使用error值来显示异常状态。...
    陈奇天阅读 2,548评论 0 6
  • error 与 try catch 对比 有人说if err写得繁琐,而我却觉得更直接/直白,好处就是维护性更好,...
    bysir阅读 13,038评论 2 8
  • 错误处理 Go语言的错误设计是通过返回值的方式来让调用者对错误进行处理,通常我们的处理是对error类型的返回值进...
    Carrism阅读 2,607评论 0 0
  • 错误处理(异常处理) 错误类型 开发过程中常见的错误: 语法错误(编译报错) 逻辑错误 (偏离开发人员本意) 运行...
    爱玩游戏的iOS菜鸟阅读 250评论 0 0