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

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è)新方法可以代替第三方的庫處理多個(gè) error,這篇文章將介紹這些變化。

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

Unwrap

如果一個(gè) err 實(shí)現(xiàn)了Unwrap函數(shù),那么errors.Unwrap會返回這個(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í)候,會采用深度優(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會把這個(gè) error 賦值給pathError變量,并且返回 true,否則返回 false。 我們這個(gè)例子正好制造的就是文件不存在的 error,所以它會輸出: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)該可以簡化成如下的方式:

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

但是,Go 不會修改這個(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
}

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

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 情況,先前,有很多的第三方庫可以供我們使用,比如

go.uber.org/multierr

github.com/hashicorp/go-multierror

github.com/cockroachdb/errors

但是現(xiàn)在,你不用再造輪子或者使用第三方庫了,因?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包變化的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

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

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

    對音視頻一直是小白,決定沉下心來,好好研究一下音視頻知識,下面這篇文章主要給大家介紹了關(guān)于Go語言學(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ì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-09-09
  • 詳解如何在Go語言中生成隨機(jī)種子

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

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

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

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

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

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

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

    在Go中,接口起到一個(gè)十分關(guān)鍵的角色,它們提供了一種方式來定義對象的行為,而不需要知道對象的具體實(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à)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • Go語言字符串操作指南:簡單易懂的實(shí)戰(zhàn)技巧

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

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

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

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

最新評論