转载
原文地址:https://www.cnblogs.com/wt645631686/p/9562997.html
Go 错误处理
Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
error类型是一个接口类型,这是它的定义:
type error interface { Error() string }
函数通常在最后的返回值中返回错误信息。使用errors.New 可返回一个错误信息:
var err = errors.New("this is an error")
错误字符串由于相对固定,一般在包作用域声明,应尽量减少在使用时直接使用 errors.New 返回。
1) errors 包
Go语言的 errors 中对 New 的定义非常简单,代码如下:
// 创建错误对象 func New(text string) error { return &errorString{text} } // 错误字符串 type errorString struct { s string } // 返回发生何种错误 func (e *errorString) Error() string { return e.s }
Golang错误处理函数defer、panic、recover
- 在默认情况下,当发生错误(panic)后,程序就会终止运行
- 如果发生错误后,可以捕获错误,并通知管理人员(邮件或者短信),程序还可以继续运行,这当然无可厚非
- errors.New("错误信息"),会返回一个error类型的值,表示一个错误
- panic内置函数,接收一个interface()类型的值(也就是任何值都可以)作为参数,可以接收error类型的变量,输出错误信息,并退出程序
- GO语言追求简洁优雅,GO语言不类似php支出try catch操作
- GO语言中引入的处理方式为:defer、panic、recover
- GO可以抛出一个panic异常。然后在defer中通过recover捕获异常再处理
自定义错误的处理与运用:
package main import ( "fmt" "errors" ) //此函数读取配置文件信息 //如果文件名不正确,返回自定义错误 func readConfFile(FileName string) (err error) { //返回error类型 if FileName == "config.ini" { return nil //表示没有错误 } else { return errors.New("读取文件错误") } } func error_func(){ err := readConfFile("config.ini1") //这里故意写错,报错代码在第一块,如果写对,在第二块 if err != nil { //如果读取文件发生错误,就输出这个错误,并终止程序 panic(err) //这个函数作用是打印错误信息,并终止程序 } fmt.Println("error_func()继续执行") } func main() { error_func() fmt.Printf("发生错误后面的代码") } //【报错信息】 //一、 // panic: 读取文件错误 // goroutine 1 [running]: // main.error_func() // D:/goproject/src/main/hello.go:22 +0xdf // main.main() // D:/goproject/src/main/hello.go:27 +0x27 // exit status 2 //二、 // error_func()继续执行 // 发生错误后面的代码
recover+defer的运用:
package main import ( "fmt" "time" ) //这里举例,在数学计算中0是不可以作为被除数的 func error_func() { //这里使用defer + recover来捕获处理异常 defer func() { //defer就是把匿名函数压入到defer栈中,等到执行完毕后或者发生异常后调用匿名函数(必须要先声明defer,否则不能捕获到panic异常) err := recover() //recover是内置函数,可以捕获到异常 if err != nil { //说明有错误 fmt.Println("err=", err) //当然这里可以把错误的详细位置发送给开发人员 //send email to admin } }() num1 := 10 num2 := 0 res := num1 / num2 fmt.Println("res=", res) } func main() { //这样程序不会轻易挂掉 error_func() i := 0 for { i++ fmt.Println("发生错误后面的代码", i) time.Sleep(time.Second) } }
defer语句是在return之后执行的,例如:
func test() (result int) { defer func() { result = 12 }() return 10 } func main() { fmt.Println(test()) // 12 }
在一个函数中panic被调用后,其defer语句仍会执行,例如:
package main import ( "fmt" "errors" ) func foo()(n int) { defer func() { if r := recover(); r != nil { fmt.Println("再然后④\n") n++ fmt.Println("最后⑤\n") } }() fmt.Println("首先①\n") n++ fmt.Printf("然后②n=%v\n" ,n) fmt.Println("然后③\n") panic(errors.New("i'm a bug")) fmt.Println("没走\n") return n } func main() { n := foo() fmt.Printf("n最后的值%v", n) }
首先① 然后②n=1 然后③ 再然后④ 最后⑤ n最后的值2
ecover 是一个Go语言的内建函数,可以让进入宕机流程中的 goroutine 恢复过来,recover 仅在延迟函数 defer 中有效,在正常的执行过程中,调用 recover 会返回 nil 并且没有其他任何效果,如果当前的 goroutine 陷入恐慌,调用 recover 可以捕获到 panic 的输入值,并且恢复正常的执行。
通常来说,不应该对进入 panic 宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,举个例子,当 web 服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。
提示
在其他语言里,宕机往往以异常的形式存在,底层抛出异常,上层逻辑通过 try/catch 机制捕获异常,没有被捕获的严重异常会导致宕机,捕获的异常可以被忽略,让代码继续运行。
Go语言没有异常系统,其使用 panic 触发宕机类似于其他语言的抛出异常,recover 的宕机恢复机制就对应其他语言中的 try/catch 机制
panic 和 recover 的关系
panic 和 recover 的组合有如下特性:
- 有 panic 没 recover,程序宕机。
- 有 panic 也有 recover,程序不会宕机,执行完对应的 defer 后,从宕机点退出当前函数后继续执行。
提示
虽然 panic/recover 能模拟其他语言的异常机制,但并不建议在编写普通函数时也经常性使用这种特性。
在 panic 触发的 defer 函数内,可以继续调用 panic,进一步将错误外抛,直到程序整体崩溃。
如果想在捕获错误时设置当前函数的返回值,可以对返回值使用命名返回值方式直接进行设置。