Golang Go语言中有了泛型,以前一直想做的错误处理终于可以实现了

发布于 1周前 作者 htzhanglong 来自 Go语言

有些错误不需要特殊处理,就能用这些简单方便的函数,有错误就 panic, 无错误就直接返回有用结果:

func Try(err error) {
	if err != nil {
		panic(err)
	}
}

func Try1[V any](val V, err error) V { Try(err) return val }

func Try2[V1 any, V2 any](val1 V1, val2 V2, err error) (V1, V2) { Try(err) return val1, val2 }

一个例子:

// 不使用泛型的传统实现
func GetExePath() string {
	path, err := os.Executable()
	if err != nil {
		panic(err)
	}
	return path
}
// 使用泛型,告别 `if err != nil`
func GetExePath() string {
	return Try1(os.Executable())
}

Golang Go语言中有了泛型,以前一直想做的错误处理终于可以实现了

更多关于Golang Go语言中有了泛型,以前一直想做的错误处理终于可以实现了的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

47 回复

比 interface{}强一点

更多关于Golang Go语言中有了泛型,以前一直想做的错误处理终于可以实现了的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


不喜欢 panic 0.0

看起来还不错

但是大部分时候不想 panic ,还是得造个 Option 好用

写过实际业务没大哥,线上也直接 panic 吗

panic 是高手

又在玩 java try catch 那套是吧 建议去写 Java

。。。。any 不就是 interface{}别名么。。

又把多态那些搞进来了吗?

一般叫 must ,你这个跟 try 有一毛钱关系吗

有使用场景的,你看看这个很流行的库也提供了这个函数 https://github.com/samber/lo#must

Must
Wraps a function call to panics if second argument is error or false, returns the value otherwise.

interface{}要转类型,泛型不用

楼上说的对,Go 里会写一个 MustXXX() 用来 panic ,比你这个 Try 好。

本身也就是多几行而已

func XXX(){}
func MustXXX(){
if XXX() != nil { panic() }
}

泛型是天生类型安全的,interface{}要自己写一堆判断才能确保类型安全。




确实,我这个函数名搞错了。原本我用的函数名是 Panic, 但看着太让人紧张于是改成 Try 。然后刚刚才知道有 lo 这个库。

我就是线上 panic😂,受了 erlang 的 let it crash 的影响



MustXXX() 一般在启动时检查配置用吧,业务里面可别 panic

对。

另外有时候时间紧或者写 demo 的时候就先 panic 偷懒,后续再好好处理错误,反正有 web 框架兜底,程序不会真的崩,向前端返回 500 而已。

500 和程序崩也一样,nginx 也是返回 502

这里的程序崩是说整个进程退出,影响了其他接口的正常使用

然而,代码也越来越难读了

这样写,PHP 不香吗?

要么 PHP 随便抛,要么 rust ,必须 处理

还有你定义错误的行数都不清楚,还是老老实实写吧,到了线上不是方便了,是找麻烦

我不是很理解

https://github.com/samber/mo 这个库更符合 lz 的想法,这是一种函数式编程的设计模式,https://en.wikipedia.org/wiki/Monad_(functional_programming)

这个就是装饰器啊,不推荐在装饰器里做异常处理,这样会导致装饰器有隐藏的顺序要求

转 go 这些人哪,入乡随俗了解一下,非要越搞越蹩脚。。。

你是个高手 233 ,go 代码 panic oncall 工单不得搞死你

想了解下楼主用 Go 的原因是啥,明明思想上无法接受。

就好像手里攥着圣经敲木鱼一样,怎么看都是别扭

Go 的整个设计都让搞 try catch 极其困难
我只会在业务逻辑里 panic 而不 recover ,以 panic 为中断整个处理的方式

我也很好奇,你为什么说我无法接受 Go 的语法?

if err != nil {panic(err)} 能处理错误,Must1(val, err) 也能处理错误,都符合 Go 的语法。

注意两点:

1. 看我的正文第一句话 “有些错误不需要特殊处理,就能用这些简单方便的函数”,我并没有说每一个错误都这样处理,只是有时候能偷懒,就这样偷懒,比如程序初始化阶段的一些很应该出错就崩溃的错误。

2. 我记得 Go 语言之父说过,error 就是一个普通的值,他建议大家用自己的方法写一些函数来处理这个值,怎么方便怎么来。

我不理解,上面很多人很鄙视用 panic 处理错误,意思是

A. 只要用了 panic, 就该被嘲笑,Go 语言就不该有 panic 这个函数。
B. 如果要用 panic, 就只能这样用 if err != nil {panic(err)},其他方法比如包裹一层 Must1(val, err) 就不行

究竟是 A 、B 哪个意思?

生产环境中确实应该尽量避免 panic ,太容易背锅,用户可不 care let it crash 这一套,他只知道这玩意儿不能用了就投诉,完了就得背一个 P0 故障,锅背多了就得走人了。。。

对呀,我也没说无脑一律 panic ,我第一句话说的就是 “有些错误不需要特殊处理,就能用这些简单方便的函数”,自己看情况用,另外我也附言给出了一个流行库,里面除了有 Must, 还有 TryOr 等多种不同的处理错误的方式,各有不同的使用场景。

唉, 大道至简, go 的抽象表达能力就只能这样, 老老实实的一行代码,三行错误处理得了, 别整些其他语言的特性, 整出来的也是不伦不类.



以前没有泛型没办法,现在原本三行错误处理可以轻松简化为一行,为什么要“老老实实”?

也没有不伦不类啊,用的都是 Go 的最最基本的语法,仅仅非常简单地包裹了一层而已,这种包裹一下变成一个方便的函数的做法,不是日常编程的常规操作吗?

不要用 try catch 替代 if 来进行数据校验呀。try catch 也好,panic 也罢,这个习惯很不好的。

各位,不要执着于是否使用 panic, 重点应该是现在有了泛型,有些错误处理可以简化了,panic 只是其中一个例子而已,上面给出了 lo 和 mo 两个库,里面有 TryOr, Option 等多种方便的函数,根据需要选用。


探讨一些啊, 有几个问题

(1) go 官方给出的 error 处理的最佳实践是啥样的

(2) go 官方给出的泛型编程的最佳实践是啥样的

(3) 如果各个开源库都有一套自己的 error 处理方式, 那么在使用的时候会不会造成障碍, 会不会额外产生一些心智负担

刚好我也想更详细一点说说这个问题。

上面我提到 “我记得 Go 语言之父说过,error 就是一个普通的值,他建议大家用自己的方法写一些函数来处理这个值,怎么方便怎么来”

凭着记忆,我找到了来源。

先看这篇发表在 Go 官方博客,Rob Pike 写的文章 https://go.dev/blog/errors-are-values

拉到文章最后,他总结道:

> Use the language to simplify your error handling.
> But remember: Whatever you do, always check your errors!

意思是,建议大家灵活使用 Go 语言去简化错误处理,只要别漏掉错误就行。

===============

然后他推荐了一篇文章 https://jxck.hatenablog.com/entry/golang-error-handling-lesson-by-rob-pike

这篇文章是日英双语的,讲述了一个活动上,博客作者 jxck 对 Go 的错误处理有疑惑,受到 Rob Pike 指导的过程。

jxck 遇到了需要写大量 if err != nil {return err} 的情况,向 Rob Pike 请教,Rob Pike 当场就给他写了一个 Wrapper, 也就是包裹了一层,把 error 先集中记录下来,后续再一次性处理。

===============

因此,Rob Pike 写了那篇官方博客,标题就是 Errors are values ,意思是不要把错误处理看成什么特殊的事情,error 就是一个普通的值,你如何对待别的任何数值、变量,就如何对待 error, 大胆去用常规编程技巧处理它。

我一直很敬佩 Rob Pike

他超过 65 岁了,但他毫不墨守成规,他敢反主流。他不会问主流是什么,如果大家不符合主流会不会沟通困难,他设计的 Go 语言就极大胆地反主流,一个新语言,静态类型的,敢没有 try-catch 处理异常,没有泛型,敢加进指针,没有标准的面向对象,而是用隐性接口,这一大堆设计都是非常大胆的。

按照上面很多人的说法,完全可以说 Go 语言本身就有一大堆不伦不类的设计。但是,为什么要这样想问题呢,连 60 多岁的老人都敢勇于“标新立异”。

============

题外话,Go 官方网站的文档 pkg.go.dev 是当今极罕见,代码不带语法高亮的,但是我们也许并未感觉有啥特别不方便。因为 Rob Pike 发现,语法高亮其实没啥用。

我自己的体验是,写代码时语法高亮有点帮助,但阅读代码时,一旦沉浸进去,是绝对感受不到有没有高亮的区别的。

op 推荐的 https://github.com/samber/do 这个 DI 库看起来不错,找机会试用下,少写点儿全局变量。

panic 没啥问题,但是一般我们都是在程序初始化的过程中如果发生问题就直接 panic 掉,比如数据目录的创建失败之类的,在程序运行过程中是极少 panic 的(几乎没有)你写这个用起来舒服,但是 log 报错的代码行数都是 Try 函数的第三行吧,这个就很麻烦。

你把错误丢给一个函数去处理,然后报错的时候,你定位不到具体代码行,这个想过吗

你看我正文第一句话 “有些错误不需要特殊处理”,如果我没想过这个函数的使用场景有限,我又怎么会说这句话呢。

还是用 nodejs 吧

在Go语言中引入泛型确实为错误处理带来了新的可能性。在泛型之前,错误处理通常依赖于接口和类型断言,这虽然灵活,但往往会导致代码冗长和难以维护。泛型的引入提供了一种更类型安全且简洁的方式来处理错误。

首先,泛型允许我们定义通用的函数和类型,这些函数和类型可以处理多种类型的错误,而不需要为每个类型单独编写代码。例如,我们可以定义一个泛型函数,该函数接受一个返回错误的结果,并对其进行统一处理。

其次,泛型还可以与自定义的错误类型结合使用,以实现更精细的错误处理逻辑。通过定义具有特定字段和方法的错误类型,我们可以利用泛型函数对这些错误进行类型安全的检查和操作,而无需担心类型断言带来的运行时错误。

此外,泛型还可以帮助我们实现更灵活的错误传播机制。在复杂的函数调用链中,我们可以使用泛型来确保错误类型在传播过程中保持一致,从而简化错误处理逻辑并提高代码的可读性。

总之,Go语言中的泛型为错误处理提供了更强大的工具。通过利用泛型,我们可以编写更简洁、类型安全且易于维护的错误处理代码。然而,需要注意的是,泛型并不是万能的,它仍然需要与其他Go语言特性(如接口和类型断言)结合使用,以实现最佳的错误处理效果。因此,在引入泛型进行错误处理时,我们需要根据具体情况进行权衡和选择。

回到顶部