首页>>后端>>Golang->Go错误处理的一些总结

Go错误处理的一些总结

时间:2023-12-01 本站 点击:0

Golang有很多优点,但是Go对错误处理的支持目前并不理想,以至于一直有一个if err != nil的梗流传于Gopher间。即便如此Gopher们也在不断的努力探索着各种优雅的解决方案。笔者就目前一些常用的解决方案进行了总结整理。

函数内错误处理

最简单粗暴的办法

通过return方式直接传给调用者进行处理。如:

if err := Foo(); err != nil {    return err}

然而,在实际使用中我们往往需要将包含一些自定义的错误信息,这是容易形成每一个err都需要判断,并且转载对应错误信息的情况。如:

// 这里传递错误信息下方法也有很多种,后续内容会提到。这里仅使用New方法举例。if err := Foo(); err != nil {    return errors.New("failed to ...")}if err := Bar(); err != nil {    return errors.New("failed to ...")}...

这时候我们就会希望能有一个集中处理错误的地方,也就自然而然的产生了下面这几种方法。

return + defer 方式

遇到错误直接return,利用defer在函数结束后统一处理。

func SomeFunc() {    var err error    defer func() {        if err != nil {            switch err {            case io.EOF:                fmt.Println(err)            case sql.ErrConnDone:                fmt.Println(err)            default:                fmt.Println("unknown err")            }            return        }        fmt.Println("I'm ok!")    }()    if err = foo(); err != nil {        return    }    if err = bar(); err != nil {        return    }}

goto + Lable方式

使用ERR在代码最后标记一个错误处理代码区,当发生错误时候,使用goto跳转到错误处理区,进行错误的集中处理。而如果代码正常运行,会直接通过return结束,并不会进入错误处理区。

func SomeFunc() {    var err error    if err = foo(); err != nil {        goto ERR    }    if err = bar(); err != nil {        goto ERR    }    returnERR:    switch err {        case io.EOF:        fmt.Println(err.Error())        case sql.ErrConnDone:        fmt.Println(err.Error())    }}

panic + recover 不推荐

遇到错误直接panic,使用recover捕获

func Run() {    defer func() {        if err := recover(); err != nil {            // 错误处理        }    }()    if err := foo(); err != nil {        panic(err)    }}

不推荐的原因:

混淆了错误和异常的概念。

开销远大于之前两种方式。

风险大,稍有不甚就会导致整个程序崩溃。

虽说不推荐,但还是把它拿出来讲,是因为这个方式可以不用每个函数都搞一个错误处理区,而是可以在顶层调用者处进行recover,统一处理。

注意: 携程之间的panic是不能通过recover捕捉到的。一个携程因为panic崩溃会导致整个程序崩溃。

func foo() {    panic("foo failed")}func Bar() {    panic("bar failed")}func SomeFunc() {    defer func() {        if err := recover(); err != nil {            fmt.Println(err)        }    }()    foo()    bar()}

错误信息的携带

主要由两个方向:一个是预定义错误;另一个是自定义错误类。

预定义错误

io包中的一些预定义错误

// ErrShortWrite means that a write accepted fewer bytes than requested// but failed to return an explicit error.var ErrShortWrite = errors.New("short write")// errInvalidWrite means that a write returned an impossible count.var errInvalidWrite = errors.New("invalid write result")// ErrShortBuffer means that a read required a longer buffer than was provided.var ErrShortBuffer = errors.New("short buffer")// EOF is the error returned by Read when no more input is available.// (Read must return EOF itself, not an error wrapping EOF,// because callers will test for EOF using ==.)// Functions should return EOF only to signal a graceful end of input.// If the EOF occurs unexpectedly in a structured data stream,// the appropriate error is either ErrUnexpectedEOF or some other error// giving more detail.var EOF = errors.New("EOF")...

自定义错误类

这是最常见的方式。将需要的信息添加到结构体中即可。

type Err struct {    Err error    Msg string    ...}

甚至还可以通过实现error接口,利用官方的error进行传递。

// The error built-in interface type is the conventional interface for// representing an error condition, with the nil value representing no error.type error interface {    Error() string}

实践方案:

注意: 自定义错误类方式往往会存在错误嵌套,我们要尽量减少不必要的包裹。以免造成错误链过长,影响性能。

Wrap方案

这是Go1.13引入的错误处理方式,据说源自于golang.org/x/xerrors

Go1.13引入了新的格式化动词: %w,用于实现Wrap效果。

fmt.Errorf("failed to login: %w", err)// 错误处理可以使用以下方法// AS: 按顺序寻找错误链中是否有与目标匹配的错误? // 第二个参数可以是任何实现了error接口的非空类型// 如果有返回true,并将err替换成错误链上第一个匹配上的// 否则返回falsefunc As(err error, target any) bool // Is: 判断错误链上是否有能匹配上的错误func Is(err, target error) bool// Unwrap 解开一层错误链func Unwrap(err error) error

一点不足:无法直接答应调用栈信息,并且Go团队也没有明确的计划。

github.com/pkg/errors

Cause方法用来判断底层错误 。

WithMessage 方法仅增加上下文文本信息,不附加调用栈。 如果确定错误已被 Wrap 过或不关心调用栈,可以使用此方法。 注意:不要反复 Wrap ,会导致调用栈重复

Wrap 方法用来包装底层错误,增加上下文文本信息并附加调用栈。 一般用于包装对第三方代码(标准库或第三方库)的调用。

// 这里传递错误信息下方法也有很多种,后续内容会提到。这里仅使用New方法举例。if err := Foo(); err != nil {    return errors.New("failed to ...")}if err := Bar(); err != nil {    return errors.New("failed to ...")}...0

\

原文:https://juejin.cn/post/7100468654663270414


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Golang/5908.html