
看 v 友们对 golang 错误处理比较感兴(si)趣(bi)( https://v2ex.com/t/921483 ),看热闹不嫌事大,更新 Golang 邪修系列,之前基于状态机实现的 try-catch-finally 。。。
项目地址: https://github.com/golang-infrastructure/go-try-catch
在其他语言中有 try-catch 的实现,但是在 Go 里面并没有提供 try-cach 的实现,更蛋疼的是 Go 里面很多操作又很容易 panic ,对 panic 的捕获很麻烦,粒度不好区分,因此就想着能不能引入一个库解决一部分这种问题。
当然这种方式并不是一个好的最佳实践,只是我们需要让业务能够正常跑起来更健壮以免隔三差五背锅,仅此而已!
最后,切记:错误应该被尽早的暴露出来,而不是一味的掩盖!
go get -u github.com/golang-infrastructure/go-try-catch 提供了两种 helper 方法,一种是比较轻量级的方法,比如当有一段代码需要执行,但是不确定会不会产生错误,就可以这个样子:
package main import ( "errors" try_catch "github.com/golang-infrastructure/go-try-catch" "github.com/stretchr/testify/assert" "testing" ) func TestTryCatch(t *testing.T) { var errFoo = errors.New("foo") // 正常执行 err := try_catch.TryCatch(func() { t.Log("ok") }) assert.Nil(t, err) // 执行时发生 panic err = try_catch.TryCatch(func() { panic(errFoo) }) assert.NotNil(t, err) assert.ErrorIs(t, err, errFoo) } 如果需要返回值的话:
func TestTryCatchReturn(t *testing.T) { var errFoo = errors.New("foo") // 正常执行 v, err := try_catch.TryCatchReturn(func() int { return 10086 }) assert.Nil(t, err) assert.Equal(t, 10086, v) // 执行时发生 panic v, err = try_catch.TryCatchReturn(func() int { panic(errFoo) }) assert.NotNil(t, err) assert.ErrorIs(t, err, errFoo) } 如果需要更多返回值:
func TryCatchReturn2[R1, R2 any](f func() (R1, R2)) (r1 R1, r2 R2, err error) func TryCatchReturn3[R1, R2, R3 any](f func() (R1, R2, R3)) (r1 R1, r2 R2, r3 R3, err error) try-catch 方法链就是定义了一些节点表示异常处理流程中的不同阶段,然后每个节点绑定对应的动作向下一阶段转移,整个状态图大概是这个样子的,其中节点是 Struct ,边是方法:

Example:
package example import ( "errors" "fmt" try_catch "github.com/golang-infrastructure/go-try-catch" "testing" ) func Test(t *testing.T) { // 正常执行 try_catch.Try(func() { fmt.Println("ok") }).Do() // try 发生异常,走 catch var errFoo = errors.New("") try_catch.Try(func() { panic(errFoo) }).Catch(errors.New("bar"), func(err error) { fmt.Println("bar") }).Catch(errFoo, func(err error) { fmt.Println("foo") }).Do() // try 发生异常,走默认 catch try_catch.Try(func() { panic(errors.New("test")) }).Catch(errors.New("bar"), func(err error) { fmt.Println("bar") }).Catch(errFoo, func(err error) { fmt.Println("foo") }).DefaultCatch(func(err error) { fmt.Println("other") }).Do() // try 未发生异常走 else try_catch.Try(func() { _ = 100 + 19 }).DefaultCatch(func(err error) { fmt.Println("other") }).Else(func() { fmt.Println("else") }).Do() // try 发生异常,并且走 finally try_catch.Try(func() { panic(errors.New("test")) }).DefaultCatch(func(err error) { fmt.Println("other") }).Else(func() { fmt.Println("else") }).Finally(func() { fmt.Println("finally") }).Do() // try 未发生异常,并且走 finally try_catch.Try(func() { _ = 100 + 19 }).DefaultCatch(func(err error) { fmt.Println("other") }).Finally(func() { fmt.Println("finally") }).Do() // 发生 panic ,尝试捕获错误,但是没有捕获得到,则异常会被向上抛出,即仍然会 panic try_catch.Try(func() { panic(errors.New("test")) }).Catch(errFoo, func(err error) { fmt.Println("catch success") }).Finally(func() { fmt.Println("not catch finally") }).Do() } 1 shynome 2023 年 3 月 7 日 via Android 已经用了蛮久了,很快就要 1.0 了 https://github.com/lainio/err2 |
2 bv 2023 年 3 月 7 日 https://github.com/golang-infrastructure/go-try-catch/blob/3e523598eac40c320f42ffc50d2356ce41d1a5b1/try_catch_func.go#L9 这些 recovery 里面都把 r 断言成了 r.(error),框架不能保证使用者 func 发生 panic 的时候传入的一定是 error 类型。这样会造成 defer recovery 里面继续 panic |
3 fregie 2023 年 3 月 7 日 via Android golang 工程特性优点 -1 |
4 FrankAdler 2023 年 3 月 7 日 via iPhone 跟自己写 recover 没区别,不建议使用+1 |
5 FreeWong 2023 年 3 月 7 日 via Android 一直觉得 判断 error 非常好 |
6 me262 2023 年 3 月 7 日 不上生产非好汉 |
8 bthulu 2023 年 3 月 7 日 你这样太麻烦了, 不如下面这样的, 只要在需要 try-catch 的代码上下加 try()和 catch()就行了,别的什么都不用干。 try() doSomething() catch(err) |
9 CC11001100 OP @bv 大佬说的是,刚看了下确实会有这个问题,感谢指正 |