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
}