关于 Go 错误处理的新提案

2024-01-07 07:11
文章标签 go 错误处理 提案

本文主要是介绍关于 Go 错误处理的新提案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

大家好,我是煎鱼。

在 Go 的编程中,错误处理机制的处理永远是大家在讨论。不过 Go1 没法大动干戈了,那就想办法继续优化吧。

今天煎鱼给大家介绍一个五一假期期间学习时看到的一个新提案。

如下图:

4a1047cc108ee77b68eaa931e132e11b.png

背景

在现阶段,我们在标准库中能够包装错误的唯一方法是使用 fmt.Errorf。可以操作的空间是比较小的。

这意味着我们对错误所能做的就是将错误内容添加到其 .Error() 输出中,以此声明 error 类型的值。

如下代码:

err := fmt.Errorf("煎鱼:%s", errors.New("放假中"))if err != nil {}

但业务诉求往往没那么简单。这个时候如果我们希望在收到错误信息时,返回堆栈并提供其他信息(例如:业务状态码)时,就没有什么特别简单的方法。

硬要做,只有如下 3 种方案:

  • 你可以返回一个新的其他错误,但会失去原始错误的上下文信息。

  • 你可以用 fmt.Errorf 来包装错误,但这只是增加了文本输出,不是调用者可以通过编程检查的东西。

  • 你可以写一个复杂的错误包装结构,其中包含你想检查的元数据,以此使用 error.Is.As.Unwrap 来工作,以允许调用者访问错误的根本原因。

现在最靠谱的是第 3 种方式,最完整,对应的是在 Go1.13 新增的 error 系列方法,还在青壮年阶段(多年来唯一新增的错误处理补全)。

提案的原作者认为现阶段还是不够简单方便。

新提案

新提案是希望在标准库 errors 中实现一个更简单的函数来达到上述第 3 点的效果,支持将任何错误与任何其他错误包装在一起,从而使它们形成一个新的包装错误列表。

如下代码:

// With returns an error that wraps err with other.  
func With(err, other error) error

这个被包裹起来的错误类似于链表,可以复用 errors.Unwrap 来遍历列表。而类链表存储,就有先后顺序的问题。

With 函数中,other 参数的错误将会放在包装错误列表的头部。如果在调用  With 函数时是 With(b->a, d->c),呈现在内的错误列表顺序将会是:d->c->b->a。

对应的使用场景:

  • errors.Is(errors.With(err, other)):

    • 判别标准:errors.Is(other) || errors.Is(err)。

  • errors.As(errors.With(err, other), target):

    • 判别标准:errors.As(other, target) || errors.As(err, target)

  • errors.With(err, other).Error():

    • 输出结果是 other.Error() + ": " + err.Error()。

提案作者@Nate Finch 希望通过这种错误包装方式,对既有的代码改动是最小的。也能提供最广泛的功能适用性,认为是有价值的。

案例

场景

作者给出了一个非常经典的用户案例。在我们平时写应用代码时,在写过的每个 go 应用程序中都看到了它。应用中有一个返回特定域错误的包,例如返回 pq.ErrNoRows 的 postgres 驱动程序。

你希望将该错误向上传递到堆栈以维护原始错误的上下文,但你不希望调用者必须知道 postgres 错误才能知道如何从存储层处理此错误。

改造

为此可以使用新的 With 函数,可以通过众所周知的错误类型添加元数据,以便可以一致地检查您的函数返回的错误,而无需关心底层实现如何。

如下代码:

// SetUserName sets the name of the user with the given id. This method returns 
// flags.NotFound if the user isn't found or flags.Conflict if a user with that
// name already exists. 
func (st *Storage) SetUserName(id uuid.UUID, name string) error {err := st.db.SetUser(id, "name="+name)if errors.Is(err, pq.ErrNoRows) {return nil, errors.With(err, flags.NotFound)}var pqErr *pq.Errorif errors.As(err, &pqErr) && pqErr.Constraint == "unique_user_name" {return errors.With(err, flags.Conflict)}if err != nil {// some other unknown errorreturn fmt.Errorf("error setting name on user with id %v: %w", err) }return nil
}

业内将这种错误称为哨兵错误。

总结

今天给大家介绍的这个提案,还是比较贴合我们日常工作中的使用场景的。平时写 Go 应用程序,思考的多,就会折腾这个问题。会出现,莫非要根据错误文本来判断错误内容?

像是业内错误库,或是之前看毛老师讲的,都会进行相关的设计。这份提案也是一个不错的补充了。

参考

  • 提案内容来自《proposal: errors: Add With(err, other error) error》

关注煎鱼,获取业内第一手消息和知识 👇

这篇关于关于 Go 错误处理的新提案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/579122

相关文章

Go标准库常见错误分析和解决办法

《Go标准库常见错误分析和解决办法》Go语言的标准库为开发者提供了丰富且高效的工具,涵盖了从网络编程到文件操作等各个方面,然而,标准库虽好,使用不当却可能适得其反,正所谓工欲善其事,必先利其器,本文将... 目录1. 使用了错误的time.Duration2. time.After导致的内存泄漏3. jsO

go中空接口的具体使用

《go中空接口的具体使用》空接口是一种特殊的接口类型,它不包含任何方法,本文主要介绍了go中空接口的具体使用,具有一定的参考价值,感兴趣的可以了解一下... 目录接口-空接口1. 什么是空接口?2. 如何使用空接口?第一,第二,第三,3. 空接口几个要注意的坑坑1:坑2:坑3:接口-空接口1. 什么是空接

JavaScript错误处理避坑指南

《JavaScript错误处理避坑指南》JavaScript错误处理是编程过程中不可避免的部分,它涉及到识别、捕获和响应代码运行时可能出现的问题,本文将详细给大家介绍一下JavaScript错误处理的... 目录一、错误类型:三大“杀手”与应对策略1. 语法错误(SyntaxError)2. 运行时错误(R

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

Go语言中最便捷的http请求包resty的使用详解

《Go语言中最便捷的http请求包resty的使用详解》go语言虽然自身就有net/http包,但是说实话用起来没那么好用,resty包是go语言中一个非常受欢迎的http请求处理包,下面我们一起来学... 目录安装一、一个简单的get二、带查询参数三、设置请求头、body四、设置表单数据五、处理响应六、超

Golang基于内存的键值存储缓存库go-cache

《Golang基于内存的键值存储缓存库go-cache》go-cache是一个内存中的key:valuestore/cache库,适用于单机应用程序,本文主要介绍了Golang基于内存的键值存储缓存库... 目录文档安装方法示例1示例2使用注意点优点缺点go-cache 和 Redis 缓存对比1)功能特性

Go 1.23中Timer无buffer的实现方式详解

《Go1.23中Timer无buffer的实现方式详解》在Go1.23中,Timer的实现通常是通过time包提供的time.Timer类型来实现的,本文主要介绍了Go1.23中Timer无buff... 目录Timer 的基本实现无缓冲区的实现自定义无缓冲 Timer 实现更复杂的 Timer 实现总结在

Go使用pprof进行CPU,内存和阻塞情况分析

《Go使用pprof进行CPU,内存和阻塞情况分析》Go语言提供了强大的pprof工具,用于分析CPU、内存、Goroutine阻塞等性能问题,帮助开发者优化程序,提高运行效率,下面我们就来深入了解下... 目录1. pprof 介绍2. 快速上手:启用 pprof3. CPU Profiling:分析 C

使用Go语言开发一个命令行文件管理工具

《使用Go语言开发一个命令行文件管理工具》这篇文章主要为大家详细介绍了如何使用Go语言开发一款命令行文件管理工具,支持批量重命名,删除,创建,移动文件,需要的小伙伴可以了解下... 目录一、工具功能一览二、核心代码解析1. 主程序结构2. 批量重命名3. 批量删除4. 创建文件/目录5. 批量移动三、如何安

Go路由注册方法详解

《Go路由注册方法详解》Go语言中,http.NewServeMux()和http.HandleFunc()是两种不同的路由注册方式,前者创建独立的ServeMux实例,适合模块化和分层路由,灵活性高... 目录Go路由注册方法1. 路由注册的方式2. 路由器的独立性3. 灵活性4. 启动服务器的方式5.