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