golang 错误处理


panic 和 recover


panic相当于throw exception,recover相当于try catch。
所以经过recover接收的异常被处理后,不会让程序crash。

func main()  {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println(r)
		}
	}()
	panic("异常信息")
}
// 异常信息

panic 和 recover 的函数签名如下:

panic(interface{})
revover()interface{} 

panic的引发

1.程序主动调用panic函数。
2.程序产生运行时错误,由运行时检测并抛出。

发生panic后,程序会从调用panic的函数位置或发生panic的地方立即返回,逐层向上执行函数的defer语句,然后逐层打印函数调用堆栈,直到被recover捕获或运行到最外层函数而退出。
不仅如此,在defer逻辑里也可以再次调用panic或抛出panic。defer 里面的 panic 能够被后续执行的 defer 捕获。


recover

recover()用来捕获panic,阻止panic 继续向上传递。recover()和defer一起使用,但是recover()只有在defer后面的函数体内被直接调用才能捕获panic终止异常,否则返回nil,异常继续向外传递。


以下场景会捕获失败:

// 这个会捕获失败
defer recover()

// 这个会捕获失败
defer fmt.Println(recover())

// 这个嵌套两层也会捕获失败
defer func() {
	func() {
		println("defer inner")
		recover()  // 无效
	} ()
} ()

以下场景会捕获成功:

defer func() {
	println("defer inner")
	recover()
} ()

func except() {
	recover()
}

func test() {
	defer except()
	panic("test panic")
}

连续多个panic抛出

可以有连续多个panic被抛出,连续多个panic的场景只能出现在defer调用里面,否则不会出现多个panic被抛出的场景。但只有最后一次panic能被捕获。例如:

func main() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	} ()
	
	// 只有最后一次panic调用能够被捕获
	defer func() {
		panic("first defer panic")
	} ()

	defer func() {
		panic("second defer panic")
	} ()

	panic("main body panic")
}

// 结果
first defer panic

新goroutine中抛出的panic

函数并不能捕获内部新启动的 goroutine 所抛出的 panic ,所以比如init函数引发的panic只能在init函数中捕获,在main中无法捕获。

例如:

func do () {
	// 这里不能捕获da函数中的panic
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	} ()
	go da()
	go db()
	time.Sleep(3 * time.Second)
}

func da() {
	panic("panic da")
	for i := 0; i < 10; i++ {
		fmt.Println(i)
	}
}

func db() {
	for i := 0; i < 10; i++ {
		fmt.Println(i)
	}
}

error


Go语言内置错误接口类型error。任何类型只要实现Error() string 方法,都可以传递error 接口类型变量。Go语言典型的错误处理方式是将error作为函数最后一个返回值。在调用函数时,通过检测其返回的error值是否为nil来进行错误处理。

type error interface {
	Error() string
}

错误处理的最佳实践

1.在多个返回值的函数,error通常作为函数最后一个返回值。
2.如果一个函数返回 error 类型变量,则先用 if 语句处理 error != nil 的异常场景,正常逻辑放到if语句块的后面,保持代码平坦。
3.defer语句应该放到err判断的后面,不然有可能产生panic。
4.在错误处理逐级向上传递的过程中,错误信息应该不断地丰富和完善,而不是简单地抛出下层调用的错误。这在错误日志分析时非常有用和友好。


错误和异常


广义上的错误:发生非期望的行为。
狭义的错误:发生非期望的已知行为,这里的已知是指错误的类型是预料并定义好的。
异常:发生非期待的未知行为。这里的未知是指错误的类型不在预先定义的范围内。又被称为未捕获的错误 (untrapped error) 。,程序编译器和运行时都没有及时将其捕获处理。而是由操作系统进行异常处理。


错误分类关系如图


在这里插入图片描述

Go是一门类型安全的语言,不会出现 untrapped error ,所以Go语言不存在所谓的异常,出现的"异常"全是错误。


Go语言的两种错误处理机制

1.通过函数返回错误类型(error)的值来处理错误。
2.通过panic打印程序调用栈,终止程序执行来处理错误。


对应的也有两条错误处理规则

1.程序发生的错误导致程序不能容错继续执行,此时程序应该主动调用panic或由运行时抛出 panic 。
2.程序虽然发生错误,但是程序能够容错继续执行,此时应该使用错误返回值的方式处理错误,或者在可能发生运行时错误的非关键分支.上使用recover捕获panic。


go的整个错误处理过程如下所示:

在这里插入图片描述

参考资料:

《go语言核心编程》

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值