错误
在Go中,函数运行失败时会返回错误信息,这些错误信息被认为是一种预期的值而非异常(exception)。Go的异常机制仅被使用在处理那些未被预料到的错误,即bug,而不是那些在健壮程序中应该被避免的程序错误。
Go使用控制流机制处理错误,编码的时候需要判断 error 的值,这样设计的原因是让应该在流程控制中处理的错误在流程控制中处理,不让错误以异常的形式抛出,当某个程序错误被当作异常处理后,这个错误会将堆栈跟踪信息返回给终端用户,这些信息不好定位错误。
错误处理策略
错误传播
子程序失败变成该函数的失败
resp, err := http.Get(url)
if err != nil{
return nil, err
}
错误传播时应提供清晰的因果链,所以通常会在返回错误的时候添加必要的信息描述
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
return nil, fmt.Errorf("parsing %s as HTML: %v", url,err)
}
重试
如果错误的发生是偶然性的,或者由不可预知的问题导致,可以进行有限次数的重试,比如请求一个接口时遇到网络波动,这时候的错误时不可预知的,但往往只要进行重试,便可以请求成功。
输出错误并结束程序
当一个错误发生后导致程序无法继续运行,这时候可以输出错误信息并结束程序。比方说在main函数初始化一些必要环境时初始化失败,此时应该直接结束程序。但是对库函数而言,应该向上传播错误,除非该错误意味着程序内部包含不一致性,即遇到了Bug,才能在库函数中结束程序。
仅输出错误信息,不中断程序
根据业务需要
忽略错误
根据业务需要
文件结尾错误(EOF)
io包保证任何由文件结束引起的读取失败都返回同一个错误——io.EOF
package io
import "errors"
// EOF is the error returned by Read when no more input is available.
var EOF = errors.New("EOF")
处理文件结束错误
for {
r, _, err := in.ReadRune()
if err == io.EOF {
break // finished reading
}
if err != nil {
return fmt.Errorf("read failed:%v", err)
}
// ...use r…
}
defer 函数
defer语句经常被用于处理成对的操作,如打开、关闭、连接、断开连接、加锁、释放锁。defer机制也常被用于记录何时进入和退出函数。
panic异常
Go的类型系统会在编译时捕获很多错误,但有些错误只能在运行时检查,如数组访问越界、空指针引用等。这些运行时错误会引起painc异常。一般而言,当panic异常发生时,程序会中断运行,并立即执行在该goroutine中被延迟的函数(defer 机制)
Go的panic 机制类似于其它语言的异常,但是panic 会引起程序的崩溃,一次 panic 一般用于严重错误。
recover 捕获异常
通常来说,不应该对panic异常做任何处理,但有些场景下需要在程序奔溃前做一些操作。比如释放资源,关闭连接等。所以可以在defer 中使用 recover 捕获异常,revover 会使程序从panic 中恢复并返回panic value 。
func Parse(input string) (s *Syntax, err error) {
defer func() {
if p := recover(); p != nil {
err = fmt.Errorf("internal error: %v", p)
}
}()
// ...parser...
}