关于 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学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

Go语言中泄漏缓冲区的问题解决

《Go语言中泄漏缓冲区的问题解决》缓冲区是一种常见的数据结构,常被用于在不同的并发单元之间传递数据,然而,若缓冲区使用不当,就可能引发泄漏缓冲区问题,本文就来介绍一下问题的解决,感兴趣的可以了解一下... 目录引言泄漏缓冲区的基本概念代码示例:泄漏缓冲区的产生项目场景:Web 服务器中的请求缓冲场景描述代码

Go语言如何判断两张图片的相似度

《Go语言如何判断两张图片的相似度》这篇文章主要为大家详细介绍了Go语言如何中实现判断两张图片的相似度的两种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 在介绍技术细节前,我们先来看看图片对比在哪些场景下可以用得到:图片去重:自动删除重复图片,为存储空间"瘦身"。想象你是一个

Go语言中Recover机制的使用

《Go语言中Recover机制的使用》Go语言的recover机制通过defer函数捕获panic,实现异常恢复与程序稳定性,具有一定的参考价值,感兴趣的可以了解一下... 目录引言Recover 的基本概念基本代码示例简单的 Recover 示例嵌套函数中的 Recover项目场景中的应用Web 服务器中

Go语言中使用JWT进行身份验证的几种方式

《Go语言中使用JWT进行身份验证的几种方式》本文主要介绍了Go语言中使用JWT进行身份验证的几种方式,包括dgrijalva/jwt-go、golang-jwt/jwt、lestrrat-go/jw... 目录简介1. github.com/dgrijalva/jwt-go安装:使用示例:解释:2. gi

go rate 原生标准限速库的使用

《gorate原生标准限速库的使用》本文主要介绍了Go标准库golang.org/x/time/rate实现限流,采用令牌桶算法控制请求速率,提供Allow/Reserve/Wait方法,具有一定... 目录介绍安装API介绍rate.NewLimiter:创建限流器limiter.Allow():请求是否

Go 语言中的 Struct Tag 的用法详解

《Go语言中的StructTag的用法详解》在Go语言中,结构体字段标签(StructTag)是一种用于给字段添加元信息(metadata)的机制,常用于序列化(如JSON、XML)、ORM映... 目录一、结构体标签的基本语法二、json:"token"的具体含义三、常见的标签格式变体四、使用示例五、使用

Ubuntu上手动安装Go环境并解决“可执行文件格式错误”问题

《Ubuntu上手动安装Go环境并解决“可执行文件格式错误”问题》:本文主要介绍Ubuntu上手动安装Go环境并解决“可执行文件格式错误”问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未... 目录一、前言二、系统架构检测三、卸载旧版 Go四、下载并安装正确版本五、配置环境变量六、验证安装七、常见

Go语言使用slices包轻松实现排序功能

《Go语言使用slices包轻松实现排序功能》在Go语言开发中,对数据进行排序是常见的需求,Go1.18版本引入的slices包提供了简洁高效的排序解决方案,支持内置类型和用户自定义类型的排序操作,本... 目录一、内置类型排序:字符串与整数的应用1. 字符串切片排序2. 整数切片排序二、检查切片排序状态:

基于Go语言实现Base62编码的三种方式以及对比分析

《基于Go语言实现Base62编码的三种方式以及对比分析》Base62编码是一种在字符编码中使用62个字符的编码方式,在计算机科学中,,Go语言是一种静态类型、编译型语言,它由Google开发并开源,... 目录一、标准库现状与解决方案1. 标准库对比表2. 解决方案完整实现代码(含边界处理)二、关键实现细