错误和异常
在golang中错误被认为是一种可以预期的结果,而异常则是一种非预期的结果
Errors
在Golang中Error是一个普通的接口,普通的值。我们可以在go的源码中找到error的定义
// http://golang.org/pkg/builtin/#error
type error interface {
Error() string
}
Error VS Exceptions
golang与其他编程相比它支持多参数返回,不像JAVA,C++,python等需要提供特殊的语句块来捕获处理异常。在Golang中通常情况下,函数的返回的最后一个参数是error,并且调用者调用了一个具有(value, error)返回值时,这是函数的调用者不能对这个value进行任何假设,必须先判定这个error的值。
Panic
golang中有panic这种机制,如果程序抛出Panic了这意味着程序挂了。不能假设调用者来解决panic,意味着代码不能继续执行。
什么时候使用panic?
对于正真意外的情况,那些表示不可恢复的程序错误,例如索引越界,不可恢复的环境问题,栈溢出等问题时我们才使用Panic。对与其他情况的错误,我们应该期望使用error来进行判断。
为什么是Errors而不是异常
- 简单。
- 应考虑失败的情况而不是成功(成功只是一时的,但是失败可能是持续的)。
- 没有隐藏的控制流(不像JAVA,C++或python需要用特定的语句块来处理意想不到的异常情况)。
- 错误处理完全交给程序员自个来控制。
- Error are value(错误只是一个普通的值)。
使用Errors的常用姿势
Sentinel Error
Sentinel error也称为哨兵错误,golang中允许我们使用特定的值来表示错误。一般是这样使用的,调用这将某个函数返回的错误值与预定义中的错误值进行等值比较。
var NotFound = errors.New("the page not found") if err == notFound { // do something }
优点:程序员可自己预先定义一些错误类型
缺点:不能携带一些上下文信息,或者使用fmt.Errof()附加一些上下文信息时,会破坏原有的错误,破坏调用者的等值判定。增大了API的表面积,使得API变得脆弱,在包与包之间创建了依赖。
Error type
Error type所想要做的事情是,程序员自定义一个错误类型,并实现error接口的Error方法即可。调用方在使用error时,对函数返回的错误做断言处理。
type MyError struct { Msg string File string Line int } func (m *MyError) Error() string { return fmt.Sprintf("%s:%d:%s", e.Msg, e.Line, e.File) } func test() error { return &MyError{"testing error", "test.go", 12} } func main() { err := test() switch err := err.(type) { case nil: // do something case *MyError: // do something default: // do something } }
优点:可以携带更多的上下文信息
缺点:调用者需要使用类型断言和类型switch将这个错误转换为我们自定的错误类型,以获取这个错误的具体信息。自定义的错误类型需要变为public。这种模型会导致和调用方产生强耦合,从而导致API变得脆弱。
不透明的error处理方法
这种错误处理方式是,程序员直接将错误进行返回,不添加任何的操作。
if err != nil { // do something }
优点:不依赖任何类型和行为
缺点:代码上会比较啰嗦
对外暴露行为,而不是错误的类型
这种错误处理方式的做法是对外不暴露细节,而是对外暴露行为(方法)。这个对外暴露的接口方法通常在这个方法里进行断言操作(即断言一个行为),最终返回一个布尔值,供调用方判定是否成功或失败。
type OpError struct { Op string Err error } type temporary interface { Temporary() bool } func (e *opError) Temporary() bool { if e.Op == "accept" && isConnErr(e.Err) { return true } if ne, ok := e.Err.(*os.SyscallError); ok { t, ok := ne.Err.(temporary) return ok && t.Temporary() } t, ok := e.Err.(temporary) return ok, && t.Temporary() } func IsTemporary(e error) bool { t, ok := e.(temporary) return ok && t.Temporary() }
优点:只断言错误的行为,而不是类型
缺点:暴露了方法
that's all