亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

錯(cuò)誤類型

  • errorString

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

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

// 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類型是一個(gè)接口類型,內(nèi)含一個(gè)Error的方法。它是錯(cuò)誤的頂級(jí)接口,實(shí)現(xiàn)了此內(nèi)置方法的結(jié)構(gòu)體都是其子類。

errorString結(jié)構(gòu)體是內(nèi)置實(shí)現(xiàn)錯(cuò)誤接口的內(nèi)置實(shí)現(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)置錯(cuò)誤類型實(shí)現(xiàn)。

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

  • wrapError

wrapError是error的另一種實(shí)現(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是另一個(gè)內(nèi)置錯(cuò)誤實(shí)現(xiàn)類,使用%w作為占位符,這里的wrapError實(shí)現(xiàn)了Unwrap方法,用戶返回內(nèi)置的err即嵌套的err。

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

  • 自定義錯(cuò)誤

實(shí)現(xiàn)自定義錯(cuò)誤非常簡(jiǎn)單,面向?qū)ο蟮奶匦詫?shí)現(xiàn)錯(cuò)誤接口erros就是實(shí)現(xiàn)了錯(cuò)誤類。安裝go語言的繼承的特性,實(shí)現(xiàn)接口對(duì)應(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())	
}

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

新建錯(cuò)誤

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

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

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

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

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

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

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

  • 實(shí)例化
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())
}

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

錯(cuò)誤解析

  • errors.Is()

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

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")
    }
}

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

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

errors.As 用于將一個(gè)錯(cuò)誤轉(zhuǎn)換為特定的錯(cuò)誤類型。如果錯(cuò)誤鏈中的某個(gè)錯(cuò)誤匹配給定的目標(biāo)類型,那么 errors.As 會(huì)將該錯(cuò)誤轉(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() 是一個(gè)用于處理嵌套或包裝錯(cuò)誤的函數(shù)。它的主要作用是提取并返回一個(gè)錯(cuò)誤的直接底層錯(cuò)誤(即被包裝的錯(cuò)誤),如果該錯(cuò)誤沒有被包裝過,則返回 nil。

package main

import (
	"errors"
	"fmt"
)

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

	// 使用 errors.Unwrap 來提取底層錯(cuò)誤
	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

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

注意:

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

錯(cuò)誤處理

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

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

go語言也提供了錯(cuò)誤捕獲recover的機(jī)制和錯(cuò)誤拋出panic機(jī)制。

  • panic

panic 是 Go 語言中的一種觸發(fā)異常處理機(jī)制的方式。當(dāng)你調(diào)用 panic 時(shí),程序會(huì)立刻停止執(zhí)行當(dāng)前函數(shù),并從調(diào)用棧中逐層向上拋出,直到找到一個(gè)適合的 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,程序會(huì)立刻中止當(dāng)前的控制流,開始回溯幀,并執(zhí)行每一層的 defer 語句。在執(zhí)行完所有的 defer 語句后,如果沒有遇到 recover,程序?qū)⒈罎?,并打?panic 的信息和堆棧跟蹤。

  • recover

recover 是一個(gè)內(nèi)建函數(shù),用于恢復(fù) panic。它只能在 defer 函數(shù)中有效。當(dāng) panic 發(fā)生時(shí),如果當(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í)行資源清理操作,并避免整個(gè)程序崩潰。

  • defer

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

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

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

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é)

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

相關(guān)文章

最新評(píng)論