为了方便实现自定义错误类型,Go语言标准库中将error定义为接口类型。比如:
typeerrorinterface{ Error()string }
按照Go语言编程习惯,error总是最后一个函数返回值,并且标准库提供了创建函数,可以方便的创建错误消息的error对象。比如:
funcdivTest(x,yint)(int,error){ ify==0{ return0,errors.New("divisionbyzero")//创建错误消息的error对象 } returnx/y,nil } funcmain(){ v,err:=divTest(3,0) iferr!=nil{ log.Fatalln(err.Error()) } println(v) }
日常开发中,我们需要根据需求自定义错误类型,可以存放更多的上下文信息,或者根据错误类型做出相应的错误处理。比如:
typeNegativeErrorstruct{ x,yint } func(NegativeError)Error()string{ return"negativevalueerror" } typeMolErrorstruct{ x,yint } func(MolError)Error()string{ return"devisionbyzero" } funcmolTest(x,MolError{x,y} } ifx<0||y<0{ return0,NegativeError{x,y} } returnx%y,nil } funcmain(){ v,err:=molTest(3,-1) iferr!=nil{ switche:=err.(type){//获取错误类型 caseMolError: println(e.x,e.y) caseNegativeError: println(e.x,e.y) default: println(e) } log.Fatalln(err.Error()) } println(v) }
与error相比,panic/recover 在应用上更类似于 try/catch 结构化。比如:
funcpanic()interface{} funcrecover()interface{}
两者区别:panic 立即中断当前函数处理流程,执行延迟调用。recover在延迟调用中可以捕获并返回panic产生的错误对象,比如:
funcMyrecover(){ iferr:=recover();err!=nil{ log.Fatalln(err) } } funcmain(){ println("start...") deferMyrecover() panic("dead") println("end...") } 输出: start... 2017/02/0911:24:13dead exitstatus1
如果有连续多次调用panic的场景,只有最后一次panic会被recover捕获处理,比如:
funcMyrecover(){ iferr:=recover();err!=nil{ log.Fatalln(err) } } funcmain(){ deferMyrecover() deferfunc(){ panic("abadproblem") }() panic("aproblem") } 输出: 2017/02/0911:31:50abadproblem exitstatus1
funcmain(){ deferMyrecover() deferlog.Println(recover()) deferprintln(recover()) panic("aproblem") } 输出: (0x0,0x0) 2016/11/1207:07:54<nil> 2016/11/1207:07:54aproblem exitstatus1
在日常开发过程中,经常需要进行调试,可以使用函数输出完整的调用栈信息,比如:
funcMyrecover(){ iferr:=recover();err!=nil{ fmt.Println(err) debug.PrintStack() //log.Fatalln(err) } } funcmain(){ deferMyrecover() panic("aproblem") } 输出: aproblem goroutine1[running]: runtime/debug.Stack(0xc42002c010,0xc42003fe20,0x1) /root/data/go/src/runtime/debug/stack.go:24+0x79 runtime/debug.PrintStack() /root/data/go/src/runtime/debug/stack.go:16+0x22 main.Myrecover() /root/data/gopath/test/panic.go:10+0x85 panic(0x48a5e0,0xc42000a320) /root/data/go/src/runtime/panic.go:458+0x243 main.main() /root/data/gopath/test/panic.go:16+0x8d
日常开发中,只有在系统发生了不可恢复性或无法正常工作的错误可以使用panic,比如端口号被占用、数据库未启动、文件系统错误等,否则不建议使用。