Golang Go语言中 邪修:Try-Catch 的 Go 实现

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

看 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-catch 的实现,但是在 Go 里面并没有提供 try-catch 的实现,更蛋疼的是 Go 里面很多操作又很容易 panic ,对 panic 的捕获很麻烦,粒度不好区分,因此就想着能不能引入一个库解决一部分这种问题。

当然这种方式并不是一个好的最佳实践,只是我们需要让业务能够正常跑起来更健壮以免隔三差五背锅,仅此而已!

最后,切记:错误应该被尽早的暴露出来,而不是一味的掩盖!

二、安装

go get -u github.com/golang-infrastructure/go-try-catch

三、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 方法链

try-catch 方法链就是定义了一些节点表示异常处理流程中的不同阶段,然后每个节点绑定对应的动作向下一阶段转移,整个状态图大概是这个样子的,其中节点是 Struct ,边是方法:

graphviz

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()

}


Golang Go语言中 邪修:Try-Catch 的 Go 实现

更多关于Golang Go语言中 邪修:Try-Catch 的 Go 实现的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

10 回复

已经用了蛮久了,很快就要 1.0 了
https://github.com/lainio/err2

更多关于Golang Go语言中 邪修:Try-Catch 的 Go 实现的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


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

golang 工程特性优点 -1

跟自己写 recover 没区别,不建议使用+1

一直觉得 判断 error 非常好

不上生产非好汉

如何把控每个地方可能出现的 error 类型?

你这样太麻烦了, 不如下面这样的, 只要在需要 try-catch 的代码上下加 try()和 catch()就行了,别的什么都不用干。
try()
doSomething()
catch(err)

大佬说的是,刚看了下确实会有这个问题,感谢指正

在Go语言中,并没有直接提供像其他编程语言(如Java、C++等)那样的try-catch异常处理机制。Go语言的设计哲学倾向于通过其他方式处理错误,比如使用返回值来报告错误。这种方式鼓励程序员显式地处理可能出现的错误情况,从而使代码更加清晰和健壮。

虽然Go没有try-catch,但你可以通过以下几种方式模拟类似的错误处理逻辑:

  1. 使用多返回值:Go函数可以返回多个值,通常最后一个返回值用于表示错误。这种方式允许调用者直接检查并处理错误。

  2. 使用panicrecover:虽然panicrecover主要用于处理运行时恐慌(如数组越界、空指针引用等),但在某些情况下,你也可以用它们来模拟异常处理。不过,这通常不是推荐的做法,因为滥用panicrecover会使代码难以理解和维护。

  3. 自定义错误处理函数:你可以编写自己的错误处理函数,将错误传递给这些函数进行处理。这种方式可以提供更灵活的错误处理策略。

总之,虽然Go语言没有try-catch机制,但通过合理使用多返回值、panicrecover以及自定义错误处理函数,你仍然可以有效地处理错误。Go语言的这种设计鼓励程序员编写更加健壮和可维护的代码。因此,建议你在编写Go代码时,尽量遵循Go语言的惯例和最佳实践。

回到顶部