为了方便实现自定义错误类型,Go语言标准库中将error定义为接口类型。比如:
type聽error聽interface{ 聽聽聽聽Error()聽string }
按照Go语言编程习惯,error总是最后一个函数返回值,并且标准库提供了创建函数,可以方便的创建错误消息的error对象。比如:
func聽divTest(x聽,y聽int)(int,聽error){ 聽聽聽聽if聽y聽==聽0{ 聽聽聽聽聽聽聽聽return聽0,聽errors.New("division聽by聽zero")聽//创建错误消息的error对象 聽聽聽聽}聽聽聽 聽聽聽聽return聽x/y,nil } func聽main(){ 聽聽聽聽v,聽err聽:=聽divTest(3,0) 聽聽聽聽if聽err聽!=聽nil{ 聽聽聽聽聽聽聽聽log.Fatalln(err.Error()) 聽聽聽聽}聽聽聽 聽聽聽聽println(v) }
日常开发中,我们需要根据需求自定义错误类型,可以存放更多的上下文信息,或者根据错误类型做出相应的错误处理。比如:
type聽NegativeError聽struct{ 聽聽聽聽x,聽y聽int } func聽(NegativeError)Error()string{ 聽聽聽聽return聽"negative聽value聽error" } type聽MolError聽struct聽{ 聽聽聽聽x,聽y聽int } func聽(MolError)Error()string{ 聽聽聽聽return聽"devision聽by聽zero" } func聽molTest(x聽,y聽int)(int,聽error){ 聽聽聽聽if聽y聽==聽0{ 聽聽聽聽聽聽聽聽return聽0,聽MolError{x,y} 聽聽聽聽} 聽聽聽聽if聽x聽<聽0聽||聽y聽<聽0{ 聽聽聽聽聽聽聽聽return聽0,聽NegativeError{x,y} 聽聽聽聽} 聽聽聽聽return聽x%y,nil } func聽main(){ 聽聽聽聽v,聽err聽:=聽molTest(3,-1) 聽聽聽聽if聽err聽!=聽nil{ 聽聽聽聽聽聽聽聽switch聽e聽:=聽err.(type){聽聽聽聽聽//获取错误类型 聽聽聽聽聽聽聽聽聽聽聽聽case聽MolError: 聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽println(e.x,e.y) 聽聽聽聽聽聽聽聽聽聽聽聽case聽NegativeError: 聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽println(e.x,e.y) 聽聽聽聽聽聽聽聽聽聽聽聽default: 聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽println(e) 聽聽聽聽聽聽聽聽} 聽聽聽聽聽聽聽聽log.Fatalln(err.Error()) 聽聽聽聽} 聽聽聽聽println(v) }
与error相比,panic/recover 在应用上更类似于 try/catch 结构化。比如:
func聽panic()聽interface{}聽聽聽 func聽recover()聽interface{}
两者区别:panic 立即中断当前函数处理流程,执行延迟调用。recover在延迟调用中可以捕获并返回panic产生的错误对象,比如:
func聽Myrecover(){ 聽聽聽聽if聽err聽:=聽recover();聽err聽!=聽nil{ 聽聽聽聽聽聽聽聽log.Fatalln(err) 聽聽聽聽}聽聽聽 } func聽main(){ 聽聽聽聽println("start...") 聽聽聽聽defer聽Myrecover() 聽聽聽聽panic("dead") 聽聽聽聽println("end...") } 输出: start... 2017/02/09聽11:24:13聽dead exit聽status聽1
如果有连续多次调用panic的场景,只有最后一次panic会被recover捕获处理,比如:
func聽Myrecover(){ 聽聽聽聽if聽err聽:=聽recover();聽err聽!=聽nil{ 聽聽聽聽聽聽聽聽log.Fatalln(err) 聽聽聽聽}聽聽聽 } func聽main(){ 聽聽聽聽defer聽Myrecover() 聽聽聽聽defer聽func(){ 聽聽聽聽聽聽聽聽panic("a聽bad聽problem") 聽聽聽聽}() 聽聽聽聽panic("a聽problem") } 输出: 2017/02/09聽11:31:50聽a聽bad聽problem exit聽status聽1
recover只有在延迟调用函数中才能得到正常工作,比如:
func聽main()聽{ 聽聽聽聽defer聽Myrecover() 聽聽聽聽defer聽log.Println(recover()) 聽聽聽聽defer聽println(recover()) 聽聽聽聽panic("a聽problem")聽 } 输出: (0x0,0x0) 2016/11/12聽07:07:54聽<nil> 2016/11/12聽07:07:54聽a聽problem exit聽status聽1
在日常开发过程中,经常需要进行调试,可以使用函数输出完整的调用栈信息,比如:
func聽Myrecover(){ 聽聽聽聽if聽err聽:=聽recover();聽err聽!=聽nil{ 聽聽聽聽聽聽聽聽fmt.Println(err) 聽聽聽聽聽聽聽聽debug.PrintStack() 聽聽聽聽聽聽聽聽//log.Fatalln(err) 聽聽聽聽} } func聽main(){ 聽聽聽聽defer聽Myrecover() 聽聽聽聽panic("a聽problem") } 输出: a聽problem goroutine聽1聽[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,比如端口号被占用、数据库未启动、文件系统错误等,否则不建议使用。
本文出自 “博学于文,约之于礼” 博客,转载请与作者联系!
有疑问加站长微信联系(非本文作者)