欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語言標(biāo)準(zhǔn)錯誤error全面解析

 更新時間:2024年10月19日 14:32:48   作者:xvwen  
Go語言中的錯誤處理是通過內(nèi)置的error接口來實現(xiàn)的,其中errorString和wrapError是兩種常見的錯誤類型實現(xiàn)方式,errorString通過errors.New()方法實現(xiàn),而wrapError則通過fmt.Errorf()方法實現(xiàn),支持錯誤的嵌套和解析

錯誤類型

  • errorString

錯誤是程序中處理邏輯和系統(tǒng)穩(wěn)定新的重要組成部分。

在go語言中內(nèi)置錯誤如下:

// The error built-in interface type is the conventional interface for// representing an error condition, with the nil value representing no error.type error interface {    Error() string}

error類型是一個接口類型,內(nèi)含一個Error的方法。它是錯誤的頂級接口,實現(xiàn)了此內(nèi)置方法的結(jié)構(gòu)體都是其子類。

errorString結(jié)構(gòu)體是內(nèi)置實現(xiàn)錯誤接口的內(nèi)置實現(xiàn),源碼如下:

// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
	return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
	s string
}

func (e *errorString) Error() string {
	return e.s
}

New方法是內(nèi)置錯誤類型實現(xiàn)。

errors.New()是最常用的錯誤類實現(xiàn)方法。

  • wrapError

wrapError是error的另一種實現(xiàn)類,位于fmt的包中,源碼如下:

// Errorf formats according to a format specifier and returns the string as a
// value that satisfies error.
//
// If the format specifier includes a %w verb with an error operand,
// the returned error will implement an Unwrap method returning the operand.
// If there is more than one %w verb, the returned error will implement an
// Unwrap method returning a []error containing all the %w operands in the
// order they appear in the arguments.
// It is invalid to supply the %w verb with an operand that does not implement
// the error interface. The %w verb is otherwise a synonym for %v.
func Errorf(format string, a ...any) error {
	p := newPrinter()
	p.wrapErrs = true
	p.doPrintf(format, a)
	s := string(p.buf)
	var err error
	switch len(p.wrappedErrs) {
	case 0:
		err = errors.New(s)
	case 1:
		w := &wrapError{msg: s}
		w.err, _ = a[p.wrappedErrs[0]].(error)
		err = w
	default:
		if p.reordered {
			slices.Sort(p.wrappedErrs)
		}
		var errs []error
		for i, argNum := range p.wrappedErrs {
			if i > 0 && p.wrappedErrs[i-1] == argNum {
				continue
			}
			if e, ok := a[argNum].(error); ok {
				errs = append(errs, e)
			}
		}
		err = &wrapErrors{s, errs}
	}
	p.free()
	return err
}

type wrapError struct {
	msg string
	err error
}

func (e *wrapError) Error() string {
	return e.msg
}

func (e *wrapError) Unwrap() error {
	return e.err
}

wrapError是另一個內(nèi)置錯誤實現(xiàn)類,使用%w作為占位符,這里的wrapError實現(xiàn)了Unwrap方法,用戶返回內(nèi)置的err即嵌套的err。

wrapError還有一個復(fù)數(shù)形式wrapErrors這里不再過多贅述。

  • 自定義錯誤

實現(xiàn)自定義錯誤非常簡單,面向?qū)ο蟮奶匦詫崿F(xiàn)錯誤接口erros就是實現(xiàn)了錯誤類。安裝go語言的繼承的特性,實現(xiàn)接口對應(yīng)的方法即可。

type error interface {
	Error() string
}
type MyErr struct {
	e string
}

func (s *MyErr) Error() string {
	return s.e
}


func main(){
	var testErr error
	testErr = &MyErr{"err"}
	fmt.Println(testErr.Error())	
}

上述代碼就是實現(xiàn)了一個自定義的error類型,注意它是一個結(jié)構(gòu)體,實際上是errorString的子類。

新建錯誤

上一小節(jié)介紹了三種錯誤類型,前兩中是內(nèi)置的錯誤類型,其中自定義的錯誤是可拓展的,可以實現(xiàn)前兩種的任意一個。

第一種errorString是實現(xiàn)比較方便,只有實現(xiàn)Error()方法;

第二種是wrapError需要實現(xiàn)兩種方法,還有一種是Unwrap()。

  • errors.New()
err := errors.New("this is a error")
fmt.Printf("----%T----%v\n", err, err)

該方法創(chuàng)建的是errorString類實例

  • fmt.Errorf()
err = fmt.Errorf("err is: %v", "no found")
fmt.Println(err)

該方法創(chuàng)建的是wrapError類實例,wrapError也是errorString的子類。

  • 實例化
type MyErr struct {
	e string
}

func (s *MyErr) Error() string {
	return s.e
}


func main(){
	var testErr error
	testErr = &MyErr{"err"}
	fmt.Println(testErr.Error())
}

由于自定義錯誤一般需要錯誤信息,所以一般直接構(gòu)造方法實例化。

錯誤解析

  • errors.Is()

errors.Is 用于判斷一個錯誤是否與另一個特定的錯誤相等。它不僅僅是簡單的比較錯誤的值,還會檢查錯誤鏈中的所有錯誤,看看它們是否與給定的目標(biāo)錯誤匹配。

package main

import (
    "errors"
    "fmt"
)

var ErrNotFound = errors.New("not found")

func findItem(id int) error {
    if id == 0 {
        return ErrNotFound
    }
    return nil
}

func main() {
    err := findItem(0)
    if errors.Is(err, ErrNotFound) {
        fmt.Println("Item not found")
    } else {
        fmt.Println("Item found")
    }
}

注意這里有一個坑,就是Is方法判斷的錯誤的類型和錯誤的信息,使用New方法即使構(gòu)建的錯誤信息相同類型不一樣也是不相等的,如下:

err1 := errors.New("err1")
err2 := errors.New("err1")
err := errors.Is(err1, err2)
fmt.Println(err) // 輸出: false
  • errors.As()

errors.As 用于將一個錯誤轉(zhuǎn)換為特定的錯誤類型。如果錯誤鏈中的某個錯誤匹配給定的目標(biāo)類型,那么 errors.As 會將該錯誤轉(zhuǎn)換為該類型,并將其賦值給目標(biāo)變量。

package main

import (
    "errors"
    "fmt"
)

type MyError struct {
    Code int
    Msg  string
}

func (e *MyError) Error() string {
    return fmt.Sprintf("code %d: %s", e.Code, e.Msg)
}

func doSomething() error {
    return &MyError{Code: 404, Msg: "Not Found"}
}

func main() {
    err := doSomething()
    var myErr *MyError
    if errors.As(err, &myErr) {
        fmt.Printf("Custom error: %v (Code: %d)\n", myErr.Msg, myErr.Code)
    } else {
        fmt.Println("Unknown error")
    }
}

可用作類型判斷。

  • errors.Unwrap()

errors.Unwrap() 是一個用于處理嵌套或包裝錯誤的函數(shù)。它的主要作用是提取并返回一個錯誤的直接底層錯誤(即被包裝的錯誤),如果該錯誤沒有被包裝過,則返回 nil。

package main

import (
	"errors"
	"fmt"
)

func main() {
	baseErr := errors.New("base error")
	wrappedErr := fmt.Errorf("wrapped error: %w", baseErr)

	// 使用 errors.Unwrap 來提取底層錯誤
	unwrappedErr := errors.Unwrap(wrappedErr)
	fmt.Println("Wrapped error:", wrappedErr)
	fmt.Println("Unwrapped error:", unwrappedErr)
}


// Output
Wrapped error: wrapped error: base error
Unwrapped error: base error

該方法只能獲取錯誤的直接內(nèi)嵌錯誤,如果要獲取更深層次的錯誤需要便利判斷。

注意:

  • errors.Is: 用于判斷一個錯誤是否與特定的錯誤值相等。適合在錯誤鏈中查找某個特定的錯誤(比如一個已知的預(yù)定義錯誤)。
  • errors.As: 用于將錯誤轉(zhuǎn)換為特定的錯誤類型。適合當(dāng)你需要根據(jù)錯誤的具體類型來處理錯誤時使用。
  • errors.Unwrap() 用于從一個包裝錯誤中提取并返回底層的錯誤。如果錯誤沒有被包裝過(或者沒有實現(xiàn) Unwrap 方法),它會返回 nil。

錯誤處理

if err := findAll(); err != nil {
	// logic
}

這個處理過程是不是很眼熟,利用錯誤解析小節(jié)的處理方法可以對錯誤判斷,進(jìn)行后續(xù)的處理。

go語言也提供了錯誤捕獲recover的機制和錯誤拋出panic機制。

  • panic

panic 是 Go 語言中的一種觸發(fā)異常處理機制的方式。當(dāng)你調(diào)用 panic 時,程序會立刻停止執(zhí)行當(dāng)前函數(shù),并從調(diào)用棧中逐層向上拋出,直到找到一個適合的 recover,或者最終導(dǎo)致程序崩潰。

package main

import "fmt"

func main() {
    fmt.Println("Start")
    panic("Something went wrong!")
    fmt.Println("End") // This line will not be executed
}

當(dāng)程序中調(diào)用了 panic,程序會立刻中止當(dāng)前的控制流,開始回溯幀,并執(zhí)行每一層的 defer 語句。在執(zhí)行完所有的 defer 語句后,如果沒有遇到 recover,程序?qū)⒈罎ⅲ⒋蛴?panic 的信息和堆棧跟蹤。

  • recover

recover 是一個內(nèi)建函數(shù),用于恢復(fù) panic。它只能在 defer 函數(shù)中有效。當(dāng) panic 發(fā)生時,如果當(dāng)前函數(shù)任何調(diào)用棧上的函數(shù)中有 defer 函數(shù)調(diào)用了 recover,那么可以捕獲 panic 的內(nèi)容,并使程序恢復(fù)正常執(zhí)行。

package main

import "fmt"

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from:", r)
        }
    }()
    fmt.Println("Start")
    panic("Something went wrong!")
    fmt.Println("End") // This line will not be executed
}

recover 通常用于確保某些代碼塊即使發(fā)生了 panic,也能執(zhí)行資源清理操作,并避免整個程序崩潰。

  • defer

defer 語句用于延遲函數(shù)的執(zhí)行,直到包含 defer 語句的函數(shù)執(zhí)行完畢時,才會執(zhí)行。這通常用于確保資源釋放或清理操作(如關(guān)閉文件、解鎖互斥鎖等)即使在函數(shù)中發(fā)生錯誤或提前返回時也會被執(zhí)行。

  • 執(zhí)行順序: 在一個函數(shù)中,你可以有多個 defer 語句。這些 defer 調(diào)用的執(zhí)行順序是后進(jìn)先出(LIFO)。也就是說,最后一個 defer 聲明的語句會最先執(zhí)行。
  • 捕獲值: defer 語句會在聲明時捕獲它引用的變量的值。也就是說,defer 語句中的參數(shù)會在聲明 defer 時計算,而不是在 defer 執(zhí)行時計算。

三個函數(shù)組合構(gòu)成了錯誤處理,如下:  

package main

import "fmt"

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()

    fmt.Println("Starting the program")
    causePanic()
    fmt.Println("Program ended gracefully")
}

func causePanic() {
    fmt.Println("About to cause panic")
    panic("A severe error occurred")
    fmt.Println("This line will not execute")
}
Starting the program
About to cause panic
Recovered from panic: A severe error occurred

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Golang使用Gin框架實現(xiàn)http分塊傳輸

    Golang使用Gin框架實現(xiàn)http分塊傳輸

    這篇文章主要為大家詳細(xì)介紹了Golang中如何使用Gin框架實現(xiàn)http分塊傳輸功能,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價值,需要的可以參考一下
    2023-05-05
  • GoLang并發(fā)編程中條件變量sync.Cond的使用

    GoLang并發(fā)編程中條件變量sync.Cond的使用

    Go標(biāo)準(zhǔn)庫提供Cond原語的目的是,為等待/通知場景下的并發(fā)問題提供支持,本文主要介紹了Go并發(fā)編程sync.Cond的具體使用,具有一定的參考價值,感興趣的可以了解一下
    2023-01-01
  • GO中的slice使用簡介(源碼分析slice)

    GO中的slice使用簡介(源碼分析slice)

    slice(切片)是go中常見和強大的類型,這篇文章不是slice使用簡介,從源碼角度來分析slice的實現(xiàn),slice的一些迷惑的使用方式,感興趣的朋友跟隨小編一起看看吧
    2023-06-06
  • Golang中堆排序的實現(xiàn)

    Golang中堆排序的實現(xiàn)

    堆是一棵基于數(shù)組實現(xiàn)的特殊的完全二叉樹,本文主要介紹了Golang中堆排序的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • 從零封裝Gin框架實現(xiàn)日志初始化及切割歸檔功能

    從零封裝Gin框架實現(xiàn)日志初始化及切割歸檔功能

    這篇文章主要為大家介紹了從零封裝Gin框架實現(xiàn)日志初始化及切割歸檔功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • go使用支付寶沙箱實現(xiàn)支付寶支付的操作步驟

    go使用支付寶沙箱實現(xiàn)支付寶支付的操作步驟

    支付寶沙箱支付是支付寶提供的一個測試環(huán)境,用于開發(fā)者在不影響真實交易的情況下進(jìn)行支付接口的開發(fā)和調(diào)試,本文給大家介紹了go使用支付寶沙箱實現(xiàn)支付寶支付的操作步驟,文中有詳細(xì)的代碼示例和圖文供大家參考,需要的朋友可以參考下
    2024-03-03
  • golang json性能分析詳解

    golang json性能分析詳解

    json格式可以算我們?nèi)粘W畛S玫男蛄谢袷街涣?,Go語言作為一個由Google開發(fā),號稱互聯(lián)網(wǎng)的C語言的語言,自然也對JSON格式支持很好。下面這篇文章主要給大家詳細(xì)分析介紹了golang json性能的相關(guān)資料,需要的朋友可以參考下。
    2018-02-02
  • Go語言讀取,設(shè)置Cookie及設(shè)置cookie過期方法詳解

    Go語言讀取,設(shè)置Cookie及設(shè)置cookie過期方法詳解

    這篇文章主要介紹了Go語言讀取,設(shè)置Cookie及設(shè)置cookie過期方法詳解,需要的朋友可以參考下
    2022-04-04
  • Golang根據(jù)job數(shù)量動態(tài)控制每秒?yún)f(xié)程的最大創(chuàng)建數(shù)量方法詳解

    Golang根據(jù)job數(shù)量動態(tài)控制每秒?yún)f(xié)程的最大創(chuàng)建數(shù)量方法詳解

    這篇文章主要介紹了Golang根據(jù)job數(shù)量動態(tài)控制每秒?yún)f(xié)程的最大創(chuàng)建數(shù)量方法
    2024-01-01
  • go語言日志記錄庫簡單使用方法實例分析

    go語言日志記錄庫簡單使用方法實例分析

    這篇文章主要介紹了go語言日志記錄庫簡單使用方法,實例分析了Go語言日志記錄的操作的技巧,需要的朋友可以參考下
    2015-03-03

最新評論