- 使用%w包装错误
还有一种方式是使用:
return nil, fmt.Errorf("another wrap err: %v", err)
%v的方式不会包装错误,所以无法追溯到源错误,但往往有时候我们会选择这种方式,而不用%w的方式 。%w的方式虽然能包装源错误,但往往我们会通过源错误去做一些处理,假如源错误被修改,那包装这个源错误的相关错误都需要做响应变化 。3、错误类型判断【golang中的错误处理】我们扩展一下上面查询课件的例子 。现在我们有这样的判断,如果传进来的id不合法我们返回400错误,如果查询数据库报错我们返回500错误,我们可以像下面这样写:
package mainimport ( "fmt" "github.com/pkg/errors")type Courseware struct { Id int64 Code string Name string}type ForbiddenError struct { Err error}func (e *ForbiddenError) Error() string { return "Forbidden: " + e.Err.Error()}func getCourseware(id int64) (*Courseware, error) { if id <= 0 {return nil, fmt.Errorf("invalid id: %d", id) } courseware, err := getFromDB(id) if err != nil {return nil, &ForbiddenError{err} } return courseware, nil}func getFromDB(id int64) (*Courseware, error) { return nil, errors.New("permission denied")}func main() { _, err := getCourseware(500) // 我们可以修改这里的id看下打印的结构 if err != nil {switch err := err.(type) {case *ForbiddenError:fmt.Println("500 err: ", err)default:fmt.Println("400 err: ", err)} }}
go run 9.go500 err:Forbidden: permission denied
这样看起来好像也没什么问题,现在我们稍微修改下代码,把上面ForbiddenError包装一下:package mainimport ( "fmt" "github.com/pkg/errors")type Courseware struct { Id int64 Code string Name string}type ForbiddenError struct { Err error}func (e *ForbiddenError) Error() string { return "Forbidden: " + e.Err.Error()}func getCourseware(id int64) (*Courseware, error) { if id <= 0 {return nil, fmt.Errorf("invalid id: %d", id) } courseware, err := getFromDB(id) if err != nil {return nil, fmt.Errorf("wrap err: %w", &ForbiddenError{err}) // 这里包装了一层错误 } return courseware, nil}func getFromDB(id int64) (*Courseware, error) { return nil, errors.New("permission denied")}func main() { _, err := getCourseware(500) if err != nil {switch err := err.(type) {case *ForbiddenError:fmt.Println("500 err: ", err)default:fmt.Println("400 err: ", err)} }}
go run 9.go400 err:wrap err: Forbidden: permission denied
可以看到我们的Forbidden错误进到了400里面,这并不是我们想要的结果 。之所以会这样,是因为在ForbiddenError的外面又包装了一层Error错误,使用类型断言的时候判断出来的是Error错误 , 所以进到了400分支 。这里我们可以使用errors.As方法,它会递归调用Unwrap方法,找到错误链中第一个与target匹配的方法:
package mainimport ( "fmt" "github.com/pkg/errors")type Courseware struct { Id int64 Code string Name string}type ForbiddenError struct { Err error}func (e *ForbiddenError) Error() string { return "Forbidden: " + e.Err.Error()}func getCourseware(id int64) (*Courseware, error) { if id <= 0 {return nil, fmt.Errorf("invalid id: %d", id) } courseware, err := getFromDB(id) if err != nil {return nil, fmt.Errorf("wrap err: %w", &ForbiddenError{err}) } return courseware, nil}func getFromDB(id int64) (*Courseware, error) { return nil, errors.New("permission denied")}func main() { _, err := getCourseware(500) if err != nil {var f *ForbiddenError // 这里实现了*ForbiddenError接口,不然会panicif errors.As(err, &f) { // 找到匹配的错误fmt.Println("500 err: ", err)} else {fmt.Println("400 err: ", err)} }}
go run 9.go500 err:wrap err: Forbidden: permission denied
4、错误值判断在代码中或者mysql库或者io库中我们经常会看到这样的全局错误:var ErrCourseware = errors.New("courseware")
这种错误我们称之为哨兵错误 。一般数据库没查到ErrNoRows或者io读到了EOF错误,这些特定的错误可以帮助我们做一些特殊的处理 。一般我们会直接用==号判断错误值,但是就像上面的如果错误被包装哪我们就不好去判断了 。好在errors包中提供了errors.Is方法 , 通过递归调用Unwrap判断错误链中是否与目标错误相匹配的错误值:
if err != nil {if errors.Is(err, ErrCourseware) {// ...} else {// ...}}
推荐阅读
- 我的世界怎么制作下界通道地狱门(我的世界中的下界传送门怎么制作)
- SLAM中的内外点
- 高等数学符号的读法 的他符号
- flutter 系列之:flutter 中的幽灵offstage
- 为什么CSS中的calc函数可能会不生效?
- 俦杌这个字念什么(山海经中的梼杌)
- 原神3.0成就众花园中的一棵核桃树怎么完成
- nginx 客户端返回499的错误码
- JS中数值类型的本质
- 可处置的 ref 结构 C# 8.0 中的 Disposable ref structs