Golang 语言基础之九: error, panic, recover
Golang 语言基础系列:
- Golang 语言基础之一: type, variable, constant
- Golang 语言基础之二: for, ifelse, switch
- Golang 语言基础之三: array, slice
- Golang 语言基础之四: map, range
- Golang 语言基础之五: function
- Golang 语言基础之六: string, pointer
- Golang 语言基础之七: struct, method
- Golang 语言基础之八: interface
- Golang 语言基础之九: error, panic, recover
- Golang 语言基础之十: goroutine, channel
很多编程语言都有异常处理机制,C++、C# 和 JAVA 中有 try{...}catch(...){...}
和 throw new SomeException()
这样的语法来捕获、处理以及抛出异常,C# 和 JAVA 还支持 try{...}catch(...){...}finally{...}
这样的语法来保证异常发生的时候执行 finally
代码块中的逻辑来做些运行时清理工作。Python 里面也有类似下面的异常机制:
在实际的项目中,对于异常的 Best Practice 很多,在使用不同的语言开发不同类型的程序时,有不同的建议。Google C++ Style 中提到 Google 内部的 C++ 代码中不使用异常,社区也有很多关于异常的讨论:
- Exception-Safe Coding in C++
- Best Practices for Exceptions
- Google C++ style guide’s No-exceptions rule; STL?
- Google C++ coding style, no exceptions rule. What about multithreading?
作为一门相对来说很新的语言,Golang 中没有使用传统的 try...catch
类似的异常处理机制,而是提供了 panic
和 recover
函数来处理所谓的 运行时异常
,也就是 Google 所称的 错误处理机制。配合 defer
语句和 error
接口可以让开发者非常灵活地处理运行时的错误和异常。官方博客的这篇文章 对此做了详细解释。
从命名可以看出,Google 肯定不希望开发者在代码中随便使用 panic
,我们知道在风险控制中,有所谓 已知的未知 和 未知的未知,在 Golang 程序设计中,对于前者,我们可以通过预先开发的代码分支来处理;对于后者就比较 tricky:
- 如果项目中的代码、使用的标准库以及第三方库在运行时内部捕获了异常并通过合适的
error
对象返回给调用者,那我们可以尽量少甚至可以不用panic
函数。 - 如果无法保证上面的情况,那为了确保程序在运行时不会因为 未知的未知 导致崩溃,那
panic
函数的使用可能不得不加在任何需要的地方。
所以,当我们开发 Library 时,panic
机制的使用或许不可避免,而在开发应用程序的时候,对于比如像 IO 异常、数据库连接异常等运行时可能会出现的异常情况,为了保证应用程序的健壮性,我们无法完全避免使用 panic
。但是我们应该认识到,恐慌
是我们和计算机都不希望看到的,应该在设计开发的时候充分考虑使用场景可能出现的情况,处理好 已知的未知,在确定需要的地方使用 panic
机制。
Error
Golang 通过支持 多返回值
让在运行时返回详细的错误信息给调用者变得非常方便。我们可以在编码中通过实现 error
接口类型来生成错误信息,error 接口的定义 如下:
还是通过下面的例子来看看:
将上面的代码存入源文件 error.go 并使用 go run error.go
可以看到下面的输入:
注释很清楚了,就不多说了。
Panic 和 recover
官方文档关于 panic 和 recover 的定义 如下:
panic
和 recover
是两个内置函数,用于处理 run-time panics 以及程序中自定义的错误。
当执行一个函数 F
的时候,如果显式地调用 panic
函数或者一个 run-time panics 发生时,F
会结束运行,所有 F
中 defer
的函数会按照 FILO 的规则被执行。之后,F
函数的调用者中 defer
的函数再被执行,如此一直到最外层代码。这时,程序已经被中断了而且错误也被一层层抛出来了,其中包括 panic
函数的参数。当前被中断的 goroutine
被称为处于 panicking
状态。由于 panic
函数的参数是空接口类型,所以可以接受任何类型的对象:
recover
函数用来获取 panic
函数的参数信息,只能在延时调用 defer
语句调用的函数中直接调用才能生效,如果在 defer
语句中也调用 panic
函数,则只有最后一个被调用的 panic
函数的参数会被 recover
函数获取到。如果 goroutine
没有 panic,那调用 recover
函数会返回 nil
。
关于错误处理,Effective Go 中做了一些很好的解释。
看一个例子:
将上面的代码存入源文件 panic.go 并使用 go run panic.go
可以看到下面的输入:
需要注意的地方都在上面的例子中有体现,注意注释和函数名字。
参考资料
- The Go Programming Language
- 学习 Go 语言 中文版
- Go in Action 中文版
- The way to Go 中文版
- Go by Example
- Organizing Go code
- Testing Techniques
- Go 语言分享
- Go 学习笔记
- Go 语言简介
- Tony Bai 的博客
声明: 本文采用 BY-NC-SA 协议进行授权. 转载请注明转自: Golang 语言基础之九: error, panic, recover
有疑问加站长微信联系(非本文作者)