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

Go 1.13中errors包的新變化示例解析

 更新時(shí)間:2023年12月20日 10:00:14   作者:晁岳攀(鳥窩) 鳥窩聊技術(shù)  
這篇文章主要為大家介紹了Go 1.13中errors包的新變化示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

Go 1.13 中 errors 包有了一些變化

這些變化是為了更好地支持 Go 的錯(cuò)誤處理提案。Go 1.20 中也增加了一個(gè)新方法,這個(gè)新方法可以代替第三方的庫(kù)處理多個(gè) error,這篇文章將介紹這些變化。

因?yàn)樵瓉淼?Go 的 errors 中的內(nèi)容非常的簡(jiǎn)單,可能會(huì)導(dǎo)致大家輕視這個(gè)包,對(duì)于新的變化不是那么的關(guān)注。讓我們一一介紹這些新的方法。

Unwrap

如果一個(gè) err 實(shí)現(xiàn)了Unwrap函數(shù),那么errors.Unwrap會(huì)返回這個(gè) err 的unwrap方法的結(jié)果,否則返回 nil。 一般標(biāo)準(zhǔn)的 error 都沒有實(shí)現(xiàn)Unwrap方法,比如io.EOF, 但是也有一小部分的 error 實(shí)現(xiàn)了Unwrap方法,比如os.PathErroros.LinkError、os.SyscallErrornet.OpError、net.DNSConfigError等等。

比如下面的代碼:

 fmt.Println(errors.Unwrap(io.EOF)) // nil
 _, err := net.Dial("tcp", "invalid.address:80")
 fmt.Println(errors.Unwrap(err))

第一行因?yàn)?code>io.EOF沒有Unwrap方法,所以輸出 nil。 net.Dial 失敗返回的 err 是*net.OpError,它實(shí)現(xiàn)了Unwrap方法,返回更底層的*net.DNSError,所以第二行輸出為lookup invalid.address: no such host

最常用的,我們使用fmt.Errorf + %w包裝一個(gè) error,比如下面的代碼:

 e1 := fmt.Errorf("e1: %w", io.EOF)
 e2 := fmt.Errorf("e2: %w + %w", e1, io.ErrClosedPipe)
 e3 := fmt.Errorf("e3: %w", e2)
 e4 := fmt.Errorf("e4: %w", e3)
 fmt.Println(errors.Unwrap(e4)) // e3: e2: e1: EOF + io: read/write on closed pipe

這段代碼逐層進(jìn)行了包裝,最后的e4包含了所有的 error,我們可以通過errors.Unwrap逐層進(jìn)行解包,直到最底層的 error。 fmt.Errorf 可以 1 一次包裝多個(gè) error,比如上面的e2,它包含了e1io.ErrClosedPipe兩個(gè) error。

我們常常在多層調(diào)用的時(shí)候,把最底層的 error 逐層包裝傳遞上去,這個(gè)時(shí)候我們可以使用fmt.Errorf + %w包裝 error。 在最高層處理 error 的時(shí)候,再逐層Unwrap解開 error,逐層處理。

Is

Is函數(shù)檢查 error 的樹中是否包含指定的目標(biāo) error。

啥是 error 的? 一個(gè) error 的數(shù)包括它本身,以及通過Unwrap方法逐層解開的 error。 error 的Unwrap方法的返回值,可能是單個(gè) error,也可能是是多個(gè) error,在返回多個(gè) error 的時(shí)候,會(huì)采用深度優(yōu)先的方式進(jìn)行遍歷檢查,尋找目標(biāo) error。

怎么才算找到目標(biāo) error 呢?一種情況就是此 err 就是目標(biāo) error,這沒有什么好說的,第二種就是此 err 實(shí)現(xiàn)了Is(err)方法,把目標(biāo) err 扔進(jìn)Is方法返回 true。

所以從功能上看Is函數(shù)其實(shí)叫做Has函數(shù)更貼切些。

下面是一個(gè)例子:

    e1 := fmt.Errorf("e1: %w", io.EOF)
    e2 := fmt.Errorf("e2: %w + %w", e1, io.ErrClosedPipe)
    e3 := fmt.Errorf("e3: %w", e2)
    e4 := fmt.Errorf("e4: %w", e3)
    fmt.Println(errors.Is(e4, io.EOF)) // true
    fmt.Println(errors.Is(e4, io.ErrClosedPipe)) // true
    fmt.Println(errors.Is(e4, io.ErrUnexpectedEOF)) // false

As

Is是遍歷 error 的數(shù),檢查是否包含目標(biāo) error。As是遍歷 error 的數(shù),檢查每一個(gè) error,看看是否可以把從 error 賦值給目標(biāo)變量,如果是,則返回 true,并且目標(biāo)變量已賦值,否則返回 false。

下面這個(gè)例子,我們可以看到As的用法:

 if _, err := os.Open("non-existing"); err != nil {
  var pathError *fs.PathError
  if errors.As(err, &pathError) {
   fmt.Println("failed at path:", pathError.Path)
  } else {
   fmt.Println(err)
  }
 }

如果 os.Open 返回的 error 的樹中包含*fs.PathError,那么errors.As會(huì)把這個(gè) error 賦值給pathError變量,并且返回 true,否則返回 false。 我們這個(gè)例子正好制造的就是文件不存在的 error,所以它會(huì)輸出:failed at path: non-existing

經(jīng)常常犯的一個(gè)錯(cuò)誤就是我們使用一個(gè)error變量作為As的第二個(gè)參數(shù)。下面這個(gè)例子 tmp 就是 error 接口類型,所以 origin 可以直接賦值給 tmp,所以errors.As返回 true,并且 tmp 的值就是 origin 的值。

 var origin = fmt.Errorf("error: %w", io.EOF)
 var tmp = io.ErrClosedPipe
 if errors.As(origin, &tmp) {
  fmt.Println(tmp) // error: EOF
 }

As使用起來總是那么別別扭扭,每次總得聲明一個(gè)變量,然后把這個(gè)變量傳遞給As函數(shù),在 Go 支持泛型之后,As應(yīng)該可以簡(jiǎn)化成如下的方式:

func As[T error](err error "T error") (T, bool)

但是,Go 不會(huì)修改這個(gè)導(dǎo)致不兼容的 API,所以我們只能繼續(xù)保留As函數(shù),增加一個(gè)新的函數(shù)是一個(gè)可行的方法,無論它叫做IsAAsOf還是AsTarget或者其他。

如果你已經(jīng)掌握了 Go 的泛型,你可以自己實(shí)現(xiàn)一個(gè)As函數(shù),比如下面的代碼:

func AsA[T error](err error "T error") (T, bool) {
 var isErr T
 if errors.As(err, &isErr) {
  return isErr, true
 }
 var zero T
 return zero, false
}

寫段測(cè)試代碼,我們可以看到它的效果:

type MyError struct{}
func (*MyError) Error() string { return "MyError" }
func main() {
 var err error = fmt.Errorf("error: %w", &MyError{})
 m, ok := AsA[*MyError](err "*MyError") // MyError does not implement error (Error method has pointer receiver)
 fmt.Println(m, ok)
}

大家在#51945[1]討論了一段時(shí)間,又是無疾而終了。

Join

在我們的項(xiàng)目中,有時(shí)候需要處理多個(gè) error,比如下面的代碼:

func (s *Server) Serve() error {
    var errs []error
    if err := s.init(); err != nil {
        errs = append(errs, err)
    }
    if err := s.start(); err != nil {
        errs = append(errs, err)
    }
    if err := s.stop(); err != nil {
        errs = append(errs, err)
    }
    if len(errs) > 0 {
        return fmt.Errorf("server error: %v", errs)
    }
    return nil
}

這段代碼中,我們需要處理三個(gè) error,如果有一個(gè) error 不為 nil,那么我們就返回 errs。 當(dāng)然,為了處理多個(gè) errors 情況,先前,有很多的第三方庫(kù)可以供我們使用,比如

go.uber.org/multierr

github.com/hashicorp/go-multierror

github.com/cockroachdb/errors

但是現(xiàn)在,你不用再造輪子或者使用第三方庫(kù)了,因?yàn)?Go 1.20 中增加了errors.Join函數(shù),它可以把多個(gè) error 合并成一個(gè) error,比如下面的代碼:

 var e1 = io.EOF
 var e2 = io.ErrClosedPipe
 var e3 = io.ErrNoProgress
 var e4 = io.ErrShortBuffer
 _, e5 := net.Dial("tcp", "invalid.address:80")
 e6 := os.Remove("/path/to/nonexistent/file")
 var e = errors.Join(e1, e2)
 e = errors.Join(e, e3)
 e = errors.Join(e, e4)
 e = errors.Join(e, e5)
 e = errors.Join(e, e6)
 fmt.Println(e.Error())
    // 輸出如下,每一個(gè)err一行
    //
 // EOF
 // io: read/write on closed pipe
 // multiple Read calls return no data or error
 // short buffer
 // dial tcp: lookup invalid.address: no such host
 // remove /path/to/nonexistent/file: no such file or directory
 fmt.Println(errors.Unwrap(e)) // nil
 fmt.Println(errors.Is(e, e6)) //true
 fmt.Println(errors.Is(e, e3)) // true
 fmt.Println(errors.Is(e, e1)) // true

你可以使用Is判斷是否包含某個(gè) error,或者使用As提取出目標(biāo) error。

參考資料

[1]

#51945: https://github.com/golang/go/issues/51945

以上就是Go 1.13中errors包的新變化示例解析的詳細(xì)內(nèi)容,更多關(guān)于Go1.13 errors包變化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go語(yǔ)言學(xué)習(xí)之將mp4通過rtmp推送流媒體服務(wù)的實(shí)現(xiàn)方法

    Go語(yǔ)言學(xué)習(xí)之將mp4通過rtmp推送流媒體服務(wù)的實(shí)現(xiàn)方法

    對(duì)音視頻一直是小白,決定沉下心來,好好研究一下音視頻知識(shí),下面這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言學(xué)習(xí)之將mp4通過rtmp推送流媒體服務(wù)的實(shí)現(xiàn)方法,需要的朋友可以參考下
    2022-12-12
  • Go之集合slice的實(shí)現(xiàn)

    Go之集合slice的實(shí)現(xiàn)

    本文主要介紹了Go之集合slice的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Golang安裝和使用protocol-buffer流程介紹

    Golang安裝和使用protocol-buffer流程介紹

    這篇文章主要介紹了Golang安裝和使用protocol-buffer過程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-09-09
  • 詳解如何在Go語(yǔ)言中生成隨機(jī)種子

    詳解如何在Go語(yǔ)言中生成隨機(jī)種子

    這篇文章主要為大家詳細(xì)介紹了如何在Go語(yǔ)言中生成隨機(jī)種子,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,有需要的小伙伴可以參考一下
    2024-04-04
  • Go語(yǔ)言刪除文本文件中的指定行操作代碼

    Go語(yǔ)言刪除文本文件中的指定行操作代碼

    假設(shè)現(xiàn)在有一個(gè)文本文件,我們需要?jiǎng)h除文件中亂碼的行。我們可以使用go的os庫(kù)來處理文件,遍歷整個(gè)文件然后將除過亂碼的行寫入一個(gè)新文件,以此來實(shí)現(xiàn)我們的需求,這篇文章主要介紹了Go語(yǔ)言刪除文本文件中的指定行,需要的朋友可以參考下
    2023-12-12
  • Golang多線程刷票的實(shí)現(xiàn)代碼

    Golang多線程刷票的實(shí)現(xiàn)代碼

    這篇文章主要介紹了Golang多線程刷票的相關(guān)資料,這里實(shí)現(xiàn)刷票的功能,對(duì)于投票,刷票的很方便,并附實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2017-07-07
  • Go語(yǔ)言常見錯(cuò)誤之將接口定義在實(shí)現(xiàn)方

    Go語(yǔ)言常見錯(cuò)誤之將接口定義在實(shí)現(xiàn)方

    在Go中,接口起到一個(gè)十分關(guān)鍵的角色,它們提供了一種方式來定義對(duì)象的行為,而不需要知道對(duì)象的具體實(shí)現(xiàn),一個(gè)常見的錯(cuò)誤是在實(shí)現(xiàn)方而不是使用方定義接口,本文將詳細(xì)探討為何這樣做是一個(gè)錯(cuò)誤,以及如何避免它
    2024-01-01
  • Go并發(fā):使用sync.WaitGroup實(shí)現(xiàn)協(xié)程同步方式

    Go并發(fā):使用sync.WaitGroup實(shí)現(xiàn)協(xié)程同步方式

    這篇文章主要介紹了Go并發(fā):使用sync.WaitGroup實(shí)現(xiàn)協(xié)程同步方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • Go語(yǔ)言字符串操作指南:簡(jiǎn)單易懂的實(shí)戰(zhàn)技巧

    Go語(yǔ)言字符串操作指南:簡(jiǎn)單易懂的實(shí)戰(zhàn)技巧

    本文將介紹Go語(yǔ)言中字符串的實(shí)戰(zhàn)操作,通過本文的學(xué)習(xí),讀者將掌握Go語(yǔ)言中字符串的常用操作,為實(shí)際開發(fā)提供幫助,需要的朋友可以參考下
    2023-10-10
  • 解決老版本goland無法調(diào)試新版本go的問題

    解決老版本goland無法調(diào)試新版本go的問題

    這篇文章主要給大家介紹了如何解決老版本goland無法調(diào)試新版本go的問題,文中通過代碼示例給大家講解的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下
    2023-11-11

最新評(píng)論