Golang漂亮的错误处理规范也是Go语言的最大亮点之一。
error接口
标准库把error定义为接口类型, 以便于自己定义错误类型
1 2 3 | type error interface{<!-- --> Error() string } |
error的使用也比较简单
1 2 3 4 5 6 7 8 | // error对象的两种创建方式 // 1.使用fmt.Errorf //var err1 error = fmt.Errorf("%s", "this is normal error") err1 := fmt.Errorf("%s", "this is normal error") fmt.Println("err1 = ", err1) // 2. 使用errors.New()创建 err2 := errors.New("this is normal error2") fmt.Println("err2 = ", err2) |
再看看error的基本使用。
对于大多数函数,如果要返回错误,大致都可以定义为如下模式,将error作为返回值的最后一个,但这并非是强制要求的:
1 2 3 | func Foo (param int) (n int, err error) {<!-- --> //... } |
调用时的代码建议按照如下的方式进行处理错误
1 2 3 4 5 6 7 | n, err := Foo(0) if err != nil {<!-- --> // 错误处理 } else {<!-- --> // 使用返回值n进行逻辑处理 } |
举个栗子说明一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | func TestUseError() {<!-- --> result, err := MyDiv(10, 0) if err != nil {<!-- --> // err不为空,那就说明出错了,那就打印错误信息 fmt.Println("err = ", err) } else {<!-- --> // err为空,那就说明没有错误,那就打印结果 fmt.Println("result = ", result) } } func MyDiv(a, b int) (res int, err error) {<!-- --> if b == 0 {<!-- --> err = errors.New("分母不能为0") } else {<!-- --> res = a / b } return } |
自定义error类型
因为Go语言接口的灵活性,根本不需要从error接口继承或者像Java一样使用implement来声明继承自哪个接口,具体代码如下:
1 2 3 4 5 6 7 8 | type pathError struct {<!-- --> Op string Path string Err error } func (e *Patherror) Error() string {<!-- --> return e.Op + " " + e.Path + ": " + e.Err.Error() } |
panic()和recover()
Go语言中有两个内置函数panic()和recover()用来报告和处理运行时错误和程序中的错误。
- error用于返回错误信息
- panic()函数用于处理运行时异常,例如下标越界,空指针等,当panic异常发生时,程序会中断运行
- recover()函数用于终止错误处理流程,也就是可以用来捕获并返回panic提交的错误内容,并使程序不要中断运行,但是recover()函数必须在defer中设置(连续调用panic,仅仅最后一个会被recover捕获)
一般recover的代码模板为:
1 2 3 4 5 6 7 8 9 | defer func() {<!-- --> if err:= recover(); err != nil {<!-- --> fmt.Println(err) } }() foo() // 无论foo()中是否会触发错误处理流程,该匿名defer函数都会在函数退出时得到执行。 // 假如foo()触发了错误处理流程,recover()函数执行将会使得该错误流程终止。 // 如果错误处理流程被触发,程序传给panic函数的参数不为nil,那么err中存的错误信息将会被打印出来。 |
下面在实践中体会一下panic和recover的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | func TestUsePanicAndRecover() {<!-- --> test1() test2(20) test3() } func test1() {<!-- --> fmt.Println("1111111111111111111") } func test2(x int) {<!-- --> //如果不想让程序崩溃,那就需要设置一个recover()函数 defer func() {<!-- --> //recover() // 直接调用recover()函数,结果就是这段代码直接跳过 // 选择打印recover(),这样就可以把panic中的错误信息打印出来 if err := recover(); err != nil {<!-- --> // 这里加一个判断,如果这里出错了,那么err就不为空,那就打印recover中的内容,否则 fmt.Println(err) } }() // () 在这里是调用匿名函数 var a [10]int a[x] = 100000 //这里会报一个数组下标越界的异常,产生一个panic,导致程序崩溃 } func test3() {<!-- --> fmt.Println("3333333333333333333333") } |