go语言是不支持异常的,go语言的设计者认为异常会被不成熟的程序员滥用,导致异常的处理过去复杂;go语言取消异常的一个替代办法是使用多返回值。
func foo(param1, param2, ...) (retval1, retval2, ...)
通常的函数实现里面,会使用一个error对象来表示是否成功,这其实类似于是否有异常发生
if val, err := foo(...); err != nil {
// exception occured
}
// normal call flow
其实我个人觉得,像java语言的异常设计的已经比较合理了,完全可以保留使用,而不是丢弃;当然C++的异常是不完美的,是应该被禁用,因为C++的异常设计有本身的缺陷,主要是C++为了兼容C而导致的兼容性问题,历史包袱啊。
go语言的异常
go语言还是保留了一个异常处理的入口,那就是panic/recover/defer
作用如下
- panic负责产生一个异常
- defer里面t通过recover来捕获异常
panic是一个内置函数,原型定义如下:
func panic(v interface{})
recover也是一个内置函数,原型定义如下:
func recover() interface{}
panic的参数是一个interface类型,recover的返回值也是一个interface类型;其实panic()的参数正好就是recover()函数的返回值,就是这么传递过去的。
举例子来说:
package main
import (
"fmt"
"reflect"
)
func foo() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("recover type =[%v]\n\tvalue=[%v]\n", reflect.TypeOf(e), reflect.ValueOf(e))
}
}()
panic(12)
}
func main() {
foo()
}
运行结果:
recover type =[int]
value=[12]
例子2:
package main
import (
"fmt"
"reflect"
)
func foo() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("recover type =[%v]\n\tvalue=[%v]\n", reflect.TypeOf(e), reflect.ValueOf(e))
}
}()
panic(fmt.Errorf("meet an error"))
}
func main() {
foo()
}
运行结果:
recover type =[*errors.errorString]
value=[meet an error]
例子3:
这是一个异常链,也就是在一个function里面panic了,然后在上层caller里面捕获这个异常:
package main
import (
"fmt"
"reflect"
)
func foo() {
panic("error in foo")
}
func main() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("recover type =[%v]\n\tvalue=[%v]\n", reflect.TypeOf(e), reflect.ValueOf(e))
}
}()
foo()
}
运行
recover type =[string]
value=[error in foo]
我们看到异常发生在foo()函数里面,但是捕获这个异常的是在main()函数里面。