Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Golang 语言怎么处理错误?

Golang 语言怎么处理错误?

作者头像
frank.
发布于 2021-03-09 10:21:41
发布于 2021-03-09 10:21:41
1.4K00
代码可运行
举报
运行总次数:0
代码可运行

01

介绍

golang 程序大多数是通过 if err != nil 处理错误,在 golang 社区中,有一部分 golang 程序员对此举是持反对观点,他们认为在 golang 代码中存在大量的错误处理代码 if err != nil,使整体代码变得非常不优雅,应该在 golang 中引入其他处理错误的机制,例如 try-catche 或其它此类处理错误的机制。其实,他们忽略了 golang 中一个特别重要的概念,即 errors are values,并且 golang 作者 Rob Pike 也对此问题做出过回应,在 golang 代码中出现重复的错误处理代码 if err != nil,可能是 golang 用户的使用方式有问题。

本文我们主要聊聊在 golang 中,怎么处理错误?

02

golang 定义错误的两种方式

使用 golang 标准库 errors 的 New() 函数,可以定义一个错误类型的变量。

func New(text string) error

New() 函数接收一个 string 类型的文本,返回一个 error 类型的变量。即使给定的文本不同,每次对 New() 函数的调用也会返回不同的错误值。

关于每次调用 New() 函数,都可以返回不同的错误值,golang 是怎么做到的呢?我们通过阅读 golang 的源码,找一下我们的问题答案。

源码 /usr/local/go/src/errors/errors.go

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
 return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
 s string
}

func (e *errorString) Error() string {
 return e.s
}

源码中,我们发现 New() 函数体中的代码是返回一个指针类型 &errorString{text},所以我们的疑问自然有了答案。

那么,golang 中定义错误的另外一种方式是什么?在 golang 标准库 fmt 中,通过调用 Errorf() 函数也可以返回一个 error 类型的错误。

源码 /usr/local/go/src/fmt/errors.go

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func Errorf(format string, a ...interface{}) error {
 p := newPrinter()
 p.wrapErrs = true
 p.doPrintf(format, a)
 s := string(p.buf)
 var err error
 if p.wrappedErr == nil {
  err = errors.New(s)
 } else {
  err = &wrapError{s, p.wrappedErr}
 }
 p.free()
 return err
}

03

错误处理方式之“不透明错误处理”

正如我们在文章开篇所述,在 golang 程序中,我们见的最多的错误处理方式就是 if err != nil,此种错误处理方式,错误处理方不关心错误提供方的错误值。因此,我们将此种错误处理方式称为“不透明错误处理”。

示例代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
err := errors.New("this is a error example")
if err != nil {
 fmt.Println(err)
  return
}

04

golang 1.13 新增 As() 函数

在 golang 1.13 中,新增 As() 函数,当 error 类型的变量是一个包装错误(wrap error)时,它可以顺着错误链(error chain)上所有被包装的错误(wrapped error)的类型做比较,直到找到一个匹配的错误类型,并返回 true,如果找不到,则返回 false。

通常,我们会使用 As() 函数判断一个 error 类型的变量是否为特定的自定义错误类型。

示例代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 自定义的错误类型
type DefineError struct {
 msg string
}

func (d *DefineError) Error() string {
 return d.msg
}

func main() {
  // wrap error
 err1 := &DefineError{"this is a define error type"}
 err2 := fmt.Errorf("wrap err2: %w\n", err1)
 err3 := fmt.Errorf("wrap err3: %w\n", err2)
 var err4 *DefineError
 if errors.As(err3, &err4) {
  // errors.As() 顺着错误链,从 err3 一直找到被包装最底层的错误值 err1,并且将 err3 与其自定义类型 `var err4 *DefineError` 匹配成功。
  fmt.Println("err1 is a variable of the DefineError type")
  fmt.Println(err4 == err1)
  return
 }
 fmt.Println("err1 is not a variable of the DefineError type")
}

05

golang 1.13 新增 Is() 函数

在 Part03 中,我们讲述了“不透明错误处理”的错误处理方式,错误处理方不关心错误提供方的错误值。但是,在错误处理方需要关心错误提供方的错误值时,错误处理方要对错误提供方的错误值进行判定,这就造成了代码的耦合,错误提供方的错误值每次修改,错误处理方都需要跟着做出相应修改。

针对这种情况,golang 一般会采用“哨兵错误处理”的错误处理方式,即定义可导出的错误变量,错误处理方和错误提供方都只操作错误变量,这样做的好处是只需维护错误变量,但是还没有彻底解决问题,如果 error 类型的错误变量是一个包装错误(wrap error),“哨兵错误处理”的错误处理方式也不方便处理该错误。

好在 golang 1.13 新增 Is() 函数,它可以顺着错误链(error chain)上所有被包装的错误(wrapped error)的类型做比较,直到找到一个匹配的错误。

示例代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 哨兵错误处理
var (
 ErrInvalidUser     = errors.New("invalid user")
 ErrNotFoundUser    = errors.New("not found user")
)

func main () {
  err1 := fmt.Errorf("wrap err1: %w\n", ErrInvalidUser)
 err2 := fmt.Errorf("wrap err2: %w\n", err1)
  // golang 1.13 新增 Is() 函数
 if errors.Is(err2, ErrInvalidUser) {
  fmt.Println(ErrInvalidUser)
  return
 }
 fmt.Println("success")
}

06

总结

本文我们开篇先是讲述了 golang 社区中,存在对待 golang 错误处理方式的反对态度的用户,这么一个客观事实。接着,我们介绍了 golang 中的两种定义错误的方式和底层源码实现,和 golang 1.13 中新增的关于错误处理的函数。如果你现在使用的是 golang 1.13 及以上版本,请使用 As()Is()

通过阅读源码 /usr/local/go/src/errors/wrap.go,我们可以发现 As()Is() 是通过在错误链中不断调用 Unwrap() 函数,最终找到匹配的错误值。其中,Unwrap() 函数也是在 golang 1.13 中新增的函数。

Unwrap() 函数的源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Unwrap returns the result of calling the Unwrap method on err, if err's
// type contains an Unwrap method returning error.
// Otherwise, Unwrap returns nil.
func Unwrap(err error) error {
 u, ok := err.(interface {
  Unwrap() error
 })
 if !ok {
  return nil
 }
 return u.Unwrap()
}

推荐阅读:

Go 语言学习之错误处理

Golang 语言的标准库 log 包怎么使用?

Golang 语言三方库 lumberjack 日志切割组件怎么使用?

Golang 语言标准库 bytes 包怎么使用?

Golang 语言的标准库 os 包怎么操作目录和文件?

参考资料: https://golang.org/pkg/errors/

延伸阅读: https://blog.golang.org/error-handling-and-go https://blog.golang.org/errors-are-values https://blog.golang.org/go1.13-errors https://golang.org/doc/tutorial/handle-errors https://medium.com/rungo/error-handling-in-go-f0125de052f0 https://www.digitalocean.com/community/tutorials/handling-errors-in-go

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-02-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Go语言开发栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Go 函数多返回值错误处理与error 类型介绍
error 接口只有一个方法,即 Error() 方法,该方法返回一个描述错误的字符串。这意味着任何实现了 Error() 方法的类型都可以被用作错误类型。通常,Go程序中的函数在遇到错误时会返回一个 error 类型的值,以便调用方可以处理或记录错误信息。
贾维斯Echo
2023/10/23
7010
Go errors
Go 语言自身的 errors:https://golang.google.cn/pkg/errors/ 包实现非常简单,使用起来非常灵活,但是又有很多不足。我们在分析 Go 语言 errors 包的同时,也介绍下一个开源的 errors 包:https://pkg.go.dev/github.com/pkg/errors。
一行舟
2022/08/25
5250
Go errors
Go 源码解读|如何用好 errors 库的 errors.Is() 与 errors.As() 方法
快一个月没有更新技术文章了,这段时间投注了较多的时间学习字节的开源项目 Kitex/Hertz ,并维护一些简单的 issue ,有兴趣的同学也可以去了解:
白泽z
2022/12/20
1K0
Go Error 嵌套到底是怎么实现的?
这句话应该怎么理解呢?翻译起来挺难的。不过从源码的角度来看,好像更容易理解其背后的含义。
AlwaysBeta
2022/01/14
3280
Golang error 的突围
姗姗来迟的 Go 1.13 修改了 errors 包,增加了几个函数,用于增强 error 的功能,这篇文章介绍 error 相关的用法。
梦醒人间
2019/09/18
1K0
Golang error 的突围
Golang语言之异常处理
在编写Go语言代码的时候,我们应该习惯使用error类型值来表明非正常的状态。作为惯用法,在Go语言标准库代码包中的很多函数和方法也会以返回error类型值来表明错误状态及其详细信息。   error是一个预定义标识符,它代表了一个Go语言內建的接口类型。这个接口的类型声明如下: type error interface{ Error() string }   其中的Error方法声明的意义就在于为方法调用方提供当前错误状态的详细信息。任何数据类型只要实现了这个可以返回string类型值的Error方法就
李海彬
2018/03/21
1.2K1
Go错误处理正确姿势
func Go(f func()){go func(){ // defer recover 捕获panic defer func(){ if err := recover(); err != nil { log.Printf("panic: %+v", err) } }() f()}()}
冬夜先生
2021/09/03
7360
Go语言中的错误处理机制
在Go语言中,错误处理主要通过内置的error接口实现。error接口是一个内置接口,定义如下:
数字扫地僧
2024/06/20
1490
Go版本大于1.13,程序里这样做错误处理才地道
之前写过几篇关于 Go 错误处理的文章,发现文章里不少知识点都有点落伍了,比如Go在1.13后对错误处理增加了一些支持,最大的变化就是支持了错误包装(Error Wrapping),以前想要在调用链路的函数里包装错误都是用"github.com/pkg/errors"这个库。
KevinYan
2023/01/03
4270
Go进阶笔记关于Error
其实很多时候是使用的姿势不对,或者说,对于error的用法没有完全理解,这里整理一下关于Go中的error 。
后场技术
2020/12/29
5090
Go Errors 错误处理
error 是一个带有 Error 方法的接口类型,这意味着你可以自己去实现这个接口:
凌虚
2020/07/19
1.2K0
Go 进阶训练营 – 错误处理二:错误定义与处理
哨兵错误,就是定义一些包级别的错误变量,然后在调用的时候外部包可以直接对比变量进行判定,在标准库当中大量的使用了这种方式。例如下方 io 库中定义的错误。
Yuyy
2022/09/13
7350
Go 进阶训练营 – 错误处理二:错误定义与处理
Go语言(golang)新发布的1.13中的Error Wrapping深度分析
Go 1.13发布的功能还有一个值得深入研究的,就是对Error的增强,也是今天我们要分析的 Error Wrapping.
sunsky
2020/08/20
2.2K0
Go 1.20 相比 Go 1.19 有哪些值得注意的改动?
Go 1.17 版本引入了一个特性,允许将 slice 转换为指向数组的指针。例如,如果有一个 slice x,你可以通过 *(*[4]byte)(x) 的方式将其转换为一个指向包含 4 个字节的数组的指针。这种转换有其局限性,它得到的是一个指针。
Piper破壳
2025/05/02
650
从零开始写一个web服务到底有多难?(三)——异常处理
Go error是一个普通的接口,通过该接口得到一个普通的值。(当然也不太普通一点是error的首字母是小写的,但是我们仍然可以在外部使用它。)
4cos90
2023/12/30
2870
Go:温故错误处理
早期Go将错误视为值的处理方式为我们服务良好。尽管标准库对错误的支持很少——只有errors.New和fmt.Errorf函数,这些函数产生的错误只包含一个消息——内置的错误接口允许Go程序员添加他们想要的任何信息。它只需要一个实现了Error方法的类型:
运维开发王义杰
2024/04/25
1630
Go:温故错误处理
聊聊golang的error增强
序 本文主要研究一下golang的error增强 OIP (96).jpeg Errors in Go 1.13 golang的1.13版本对error进行了增强,主要是 引入了Unwrap方法 增加Is和As方法 fmt.Errorf支持%w来包装error 实例 package main import ( "errors" "fmt" "os" ) var DemoErr = errors.New("test error stack") // https://itnex
code4it
2020/12/01
3150
聊聊golang的error增强
Go 错误和异常
程序运行过程中难免会产生错误和异常,Java、JavaScript、PHP、Python等语言都是通过try catch(e Exception){}范式去处理,但是Go语言不同。接下来我们学习一下Go语言中的错误(error)和异常(painc)处理。
一行舟
2022/08/25
5320
golang当中的错误处理--筑基五层
在前面的几篇文章当中,我们主要是学习了Golang当中文件的读写以及数据的编码方式相关的知识。接下来,我们将开始来学习Golang中的错误处理。
闻说社
2025/05/13
1040
golang当中的错误处理--筑基五层
『Go 内置库第一季:error』
Go 中的错误处理和别的语言不一样,设计哲学也不一样,常有开发者埋怨 Go 语言中的错误处理。
谢伟
2018/12/10
4320
相关推荐
Go 函数多返回值错误处理与error 类型介绍
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验