go语言处理错误的正确姿势

Go语言处理错误的正确姿势

在Go语言中,错误处理是通过内置的error类型来实现的,而异常则是通过panicrecover函数来处理。

Error

error是一个内置的接口类型,它的定义如下:

type error interface {
    Error() string
}

这是一个简单的接口,只要你的类型实现了Error方法(这个方法会返回一个字符串),那么它就实现了error接口。在函数中,如果出现了错误情况,通常会返回一个实现了error接口的值。

Go语言的标准库中已经提供了几个实现了error接口的结构,例如errors.New函数返回的*errors.errorString类型,以及fmt.Errorf函数返回的*errors.errorString类型。

你也可以定义自己的错误类型,只要这个类型实现了error接口就可以。

type MyError struct {
  Message string
  Err     error
}

func (e *MyError) Error() string {
  return e.Message
}

Exception

Go语言没有类似Java或者Python那样的异常机制,但是它提供了panic和recover两个函数,可以在遇到无法恢复的错误状态的时候,终止函数执行。简单来说,panic用于产生异常,而recover用于捕获异常。

panic函数接受一个实现了error接口的对象,或者是一个interface{}类型的对象。调用panic函数会立即停止函数的执行,并且运行defer语句。

recover函数用于捕获由panic函数引起的异常。recover函数只能在defer语句中使用。在正常执行过程中,调用recover会返回nil并且没有其他效果;但是如果当前的goroutine在恐慌中(已经调用过panic函数),调用recover可以捕获到panic的输入值,并且恢复正常的执行。

func main() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("Recovered from", err)
        }
    }()
    panic("A severe error occurred: stopping the program!")
}

在上面的例子中,当panic函数被调用(可能是由于一些严重的错误导致的)时,执行会立即停止,然后执行defer函数。在defer函数里面,我们调用了recover函数,捕获到了panic的输入值,之后函数就可以正常的执行下去了。

如何优雅的处理错误

    Go语言鼓励开发人员对每个错误进行明确的处理。这是Go语言对异常处理的设计哲学,目标是在函数签名上让潜在的错误变得明显。这种方式鼓励开发者更早更显眼的注意到错误处理,而不是像某些语言那样抛出异常然后在别的地方处理。

    优雅处理Error的一些方法包括:

  • 使用errors包中的函数产生简单的纯错误文本信息。

  • 使用fmt.Errorf生成错误信息,并包含有用的变量值。

  • 创建自定义错误类型,可以包含更多信息,比如错误的上下文,使得错误更具有信息价值。

    在Go中,错误被视为一等公民,并且Go鼓励你明确处理每一个可能的错误。虽然这可能会让代码看起来有些繁琐,但是这也会让你的应用更加稳健,因为你很难忽略处理错误。

 

一、定义返回错误

func LowerLevelFunction() (string, error) {
    // 执行一些操作
    return result, err
}

func HigherLevelFunction() {
    result, err := LowerLevelFunction()
    if err != nil {
        // 处理错误
    } else {
        // 使用结果
    }
}

实际上,Go语言中的错误处理就是这样简单直接。你需要在你的函数中返回一个错误,并在接收到错误时处理它。几乎所有的 return 语句都包含了一个错误值,这就是 Go 语言让错误处理显而易见的方式。

二、封装错误继续抛

_, err := os.Open("non-existing-file")
if err != nil {
    return fmt.Errorf("failed to open file: %w", err)
}

上述代码中的%w标志符在Go 1.13版本被引入,它允许将原始错误包装在新的错误中。

你也可以创建自定义错误类型,提供更多上下文信息。例如:

type customError struct {
  originalError error
  contextInfo   string
}

func (ce customError) Error() string {
    return fmt.Sprintf("failed: %s: %v", ce.contextInfo, ce.originalError)
}

_, err := os.Open("non-existing-file")
if err != nil {
    return customError{
    originalError: err,
    contextInfo:   "failed to open file",
  }
}

在这个例子中,customError包括了原始的错误和额外的上下文信息。并且,customError实现了Error()方法,使其满足error接口。

总的来说,包装错误并向上抛出可以帮助我们提供更多的错误上下文,以便于更好地排查问题。在Go 1.13及以上的版本,我们还可以使用errors.Iserrors.As去检查和获取原始的错误。

errors.Is函数用于检测一个错误是否与特定的错误相同。它会考虑错误的原始类型和通过fmt.Errorf包装的错误。

_, err := os.Open("non-existing-file")
if errors.Is(err, os.ErrNotExist) {
    fmt.Println("file does not exist")
} else {
    fmt.Println("other error")
}

以上的代码,如果打开文件的错误是因为文件不存在(os.ErrNotExist),则返回文件不存在的错误信息,否则返回其他错误。

errors.As函数用于将错误转为特定的错误类型,如果转换失败,将返回 false。

_, err := os.Open("non-existing-file")
if pathError, ok := err.(*os.PathError); ok {
    fmt.Println("failed at path:", pathError.Path)
} else {
    fmt.Println("other error")
}

以上的代码,如果打开文件的错误是*os.PathError类型的错误,则获取失败的文件路径。如果不是这种类型的错误,则返回其他错误。

 

收录于合集 #go语言
 58
上一篇Go如何实现分布式锁下一篇Go语言实现时间窗口限流器
个人观点,仅供参考
阅读 429
程序员技术成长之路
 
关注公众号后可以给作者发消息
 
 
 
 
 
 
 
posted @   技术颜良  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)
历史上的今天:
2022-11-07 Go进阶53:从零Go实现Websocket-H5-RDP/VNC远程桌面客户端
2022-11-07 golang-ssh-01:执行远程命令
点击右上角即可分享
微信分享提示