Go语言的错误处理设计思想中主张

1.如果一个函数可能出现异常,那么应该把异常作为返回值,没有异常就返回 nil

2.每次调用可能出现异常的函数时,都应该主动进行检查,并做出反应,这种 if 语句术语叫 卫述语句

所以异常应该总是掌握在我们的手上,保证每次操作产生的影响达到最小,保证程序即使部分地方出现问题,也不会影响整个程序的运行,及时的处理异常,这样就可以减轻上层处理异常的压力,同时也不要让未知的异常使你的程序崩溃

我们应该让异常以这样的形式出现

func Demo() (int, error)

我们应该让异常以这样的形式处理(卫述语句)

_,err := errorDemo() 
if err!=nil{
 fmt.Println(err)
 return
}

 

下面我们来说下如何使用和定义异常处理

errors-根据 业务 自定义错误信息

Go 语言通过内置的错误接口提供了非常简单的错误处理机制

我们可以在编码中通过实现 error 接口类型来生成错误信息(go 内置的一些函数里面,可直接通过 err 获取错误信息)。

如果一个函数需要根据业务逻辑返回错误信息,可使用 errors.New 返回一个自定义的错误:

func Sqrt(f float64) (float64, error) {
    if f < 0 {
        return 0, errors.New("math: square root of negative number")
    } 
}

func Test()
 result, err:= Sqrt(-1) //通过 err 获取方法出错时返回的 errors.New 的内容
 if err != nil { 
  fmt.Println(err.Error()) //打印异常信息
}

 

panic-系统中断异常

主要使用场景

1.发生严重错误必须让进行退出:Go 的类型系统会在编译时捕获很多异常,但有些异常只能在运行时检查,如数组访问越界、空指针引用等。这些运行时异常会引起 painc 异常(程序直接崩溃退出),这个时候哪怕是执行下去程序也是毫无意义的,这个时候 panic 暴露出问题反而是更可取的方式

2.快速退出错误处理:大多数情况下错误处理都应该使用判断 error 的机制,但是有时函数调用栈很深,逐层返回错误可能需要写很多冗余代码,这个时候可以使用 panic 让程序的控制流直接跳到顶层的 recover 来进行处理。这种场景需要注意必须在包内就要 recover。让 panic 垮包传递可能会导致更复杂的问题,所以包的到处函数不应该产生 panic

 

recover-捕获panic

recover 是一个内建的函数,用于重新获得 panic 协程的控制。 只有在延迟函数的内部(需要使用 defer 修饰),调用 recover 才有用

在延迟函数内调用 recover,可以取到 panic 的错误信息,并且停止 panic 续发事件(Panicking Sequence),程序运行恢复正常(目的是让程序不崩溃,进而可以将 panic 异常转为普通 error 异常进行写入日志或者返回错误信息等操作)。如果在延迟函数的外部调用 recover,就不能停止 panic 续发事件。

func main() {
	n := 0
	val, err := errTest(n)
	if err != nil { //由于 panic 之前没有对 err 进行赋值,所以 err 为 nil
		fmt.Print(err.Error())
	}
	fmt.Println(n)
}

func errTest(n int) (int, error) {
        //被 defer 修饰的函数不会马上执行,而是会等到该函数执行完成后才会执行,执行顺序为倒序
	defer func() {
                //recover 会捕获当前函数的 panic 异常,如果有,则进行处理
		if err := recover(); err != nil { 
			fmt.Println("defer",err)
		}
	}()
	if n == 0 {
                //err=fmt.Errorf("errTest error")  
		panic("panic error") //调用 panic 异常,当前函数直接退出,不做任何返回
	}
	return n, nil
}