go语言中的error以及错误处理(defer,panic,recover)


转载

原文地址: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

  1. 在默认情况下,当发生错误(panic)后,程序就会终止运行
  2. 如果发生错误后,可以捕获错误,并通知管理人员(邮件或者短信),程序还可以继续运行,这当然无可厚非
  3. errors.New("错误信息"),会返回一个error类型的值,表示一个错误
  4. panic内置函数,接收一个interface()类型的值(也就是任何值都可以)作为参数,可以接收error类型的变量,输出错误信息,并退出程序
  5. GO语言追求简洁优雅,GO语言不类似php支出try catch操作
  6. GO语言中引入的处理方式为:defer、panic、recover
  7. 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,进一步将错误外抛,直到程序整体崩溃。
如果想在捕获错误时设置当前函数的返回值,可以对返回值使用命名返回值方式直接进行设置。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM