<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Go Error 嵌套到底是怎么實現的?

      原文鏈接: Go Error 嵌套到底是怎么實現的?

      Go Error 的設計哲學是 「Errors Are Values」。

      這句話應該怎么理解呢?翻譯起來挺難的。不過從源碼的角度來看,好像更容易理解其背后的含義。

      Go Error 源碼很簡單,寥寥幾行:

      // src/builtin/builtin.go
      
      type error interface {
      	Error() string
      }
      

      error 是一個接口類型,只需要實現 Error() 方法即可。在 Error() 方法中,就可以返回自定義結構體的任意內容。

      下面首先說說如何創建 error。

      創建 Error

      創建 error 有兩種方式,分別是:

      1. errors.New();
      2. fmt.Errorf()

      errors.New()

      errors.New() 的使用延續了 Go 的一貫風格,New 一下就可以了。

      舉一個例子:

      package main
      
      import (
      	"errors"
      	"fmt"
      )
      
      func main() {
      	err := errors.New("這是 errors.New() 創建的錯誤")
      	fmt.Printf("err 錯誤類型:%T,錯誤為:%v\n", err, err)
      }
      
      /* 輸出
      err 錯誤類型:*errors.errorString,錯誤為:這是 errors.New() 創建的錯誤
      */
      

      這段代碼唯一讓人困惑的地方可能就是錯誤類型了,但沒關系。只要看一下源碼,就瞬間迎刃而解。

      源碼如下:

      // src/errors/errors.go
      
      // 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
      }
      

      可以看到,errorString 是一個結構體,實現了 Error() 方法,New 函數直接返回 errorString 指針。

      這種用法很簡單,但不實用。假如我還想返回程序的上下文信息,它就沒轍了。

      下面看第二種方式。

      fmt.Errorf()

      還是先看一個例子:

      package main
      
      import (
      	"database/sql"
      	"fmt"
      )
      
      func foo() error {
      	return sql.ErrNoRows
      }
      
      func bar() error {
      	return foo()
      }
      
      func main() {
      	err := bar()
      	if err == sql.ErrNoRows {
      		fmt.Printf("data not found, %+v\n", err)
      		return
      	}
      	if err != nil {
      		fmt.Println("Unknown error")
      	}
      }
      
      /* 輸出
      data not found, sql: no rows in result set
      */
      

      這個例子輸出了我們想要的結果,但是還不夠。

      一般情況下,我們會通過使用 fmt.Errorf() 函數,附加上我們想添加的文本信息,使返回內容更明確,處理起來更靈活。

      所以,foo() 函數會改成下面這樣:

      func foo() error {
         return fmt.Errorf("foo err, %v", sql.ErrNoRows)
      }
      

      這時問題就出現了,經過 fmt.Errorf() 的封裝,原始 error 類型發生了改變,這就導致 err == sql.ErrNoRows 不再成立,返回信息變成了 Unknown error

      如果想根據返回的 error 類型做不同處理,就無法實現了。

      因此,Go 1.13 為我們提供了 wrapError 來處理這個問題。

      Wrap Error

      看一個例子:

      package main
      
      import (
      	"fmt"
      )
      
      type myError struct{}
      
      func (e myError) Error() string {
      	return "Error happended"
      }
      
      func main() {
      	e1 := myError{}
      	e2 := fmt.Errorf("E2: %w", e1)
      	e3 := fmt.Errorf("E3: %w", e2)
      	fmt.Println(e2)
      	fmt.Println(e3)
      }
      
      /* output
      E2: Error happended
      E3: E2: Error happended
      */
      

      乍一看好像好沒什么區別,但背后的實現原理卻并不相同。

      Go 擴展了 fmt.Errorf() 函數,增加了一個 %w 標識符來創建 wrapError

      // src/fmt/errors.go
      
      func Errorf(format string, a ...interface{}) error {
      	p := newPrinter()
      	p.wrapErrs = true
      	p.doPrintf(format, a)
      	s := string(p.buf)
      	var err error
      	if p.wrappedErr == nil {
      		err = errors.New(s)
      	} else {
      		err = &wrapError{s, p.wrappedErr}
      	}
      	p.free()
      	return err
      }
      

      當使用 w% 時,函數會返回 &wrapError{s, p.wrappedErr},wrapError 結構體定義如下:

      // src/fmt/errors.go
      
      type wrapError struct {
      	msg string
      	err error
      }
      
      func (e *wrapError) Error() string {
      	return e.msg
      }
      
      func (e *wrapError) Unwrap() error {
      	return e.err
      }
      

      實現了 Error() 方法,說明它是一個 error,而 Unwrap() 方法是為了獲取被封裝的 error。

      // src/errors/wrap.go
      
      func Unwrap(err error) error {
      	u, ok := err.(interface {
      		Unwrap() error
      	})
      	if !ok {
      		return nil
      	}
      	return u.Unwrap()
      }
      

      它們之間的關系是這樣的:

      因此,我們可以使用 w% 將上文中的程序進行改造,使其內容輸出更豐富。

      如下:

      package main
      
      import (
      	"database/sql"
      	"errors"
      	"fmt"
      )
      
      func bar() error {
      	if err := foo(); err != nil {
      		return fmt.Errorf("bar failed: %w", foo())
      	}
      	return nil
      }
      
      func foo() error {
      	return fmt.Errorf("foo failed: %w", sql.ErrNoRows)
      }
      
      func main() {
      	err := bar()
      	if errors.Is(err, sql.ErrNoRows) {
      		fmt.Printf("data not found,  %+v\n", err)
      		return
      	}
      	if err != nil {
      		fmt.Println("Unknown error")
      	}
      }
      
      /* output
      data not found,  bar failed: foo failed: sql: no rows in result set
      */
      

      終于有了讓人滿意的輸出結果,每個函數都增加了必要的上下文信息,而且也符合對錯誤類型的判斷。

      errors.Is() 函數用來判斷 err 以及其封裝的 error 鏈中是否包含目標類型。這也就解決了上文提出的無法判斷錯誤類型的問題。

      后記

      其實,Go 目前對 Error 的處理方式也是充滿爭議的。不過,官方團隊正在積極和社區交流,提出改進方法。相信在不久的將來,一定會找到更好的解決方案。

      現階段來說,大部分團隊可能會選擇 github.com/pkg/errors 包來進行錯誤處理。如果感興趣的話,可以學學看。

      好了,本文就到這里吧。關注我,帶你通過問題讀 Go 源碼。


      源碼地址:

      推薦閱讀:

      參考文章:

      posted @ 2022-01-14 14:04  yongxinz  閱讀(309)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 色一伊人区二区亚洲最大| 国产免费一区二区不卡| 亚洲区综合区小说区激情区| 无套内谢少妇一二三四| 日韩有码中文字幕国产| 丰满人妻被黑人连续中出 | 部精品久久久久久久久| 粉嫩蜜臀av一区二区三区| 四川少妇被弄到高潮| 国产专区一线二线三线码| 秋霞A级毛片在线看| 深夜福利资源在线观看| 蜜臀91精品国产高清在线| 国精产品999国精产| 毛片内射久久久一区| 无卡无码无免费毛片| 人人入人人爱| 日韩精品有码中文字幕| 伊春市| 97se亚洲综合自在线| 97精品久久九九中文字幕| 亚洲首页一区任你躁xxxxx| 肥臀浪妇太爽了快点再快点| 国产精品三级爽片免费看| 国产成人午夜在线视频极速观看| 国产成人亚洲日韩欧美| 久久蜜臀av一区三区| 丰满少妇内射一区| 亚洲精品岛国片在线观看| 亚洲人成精品久久久久| 国产另类ts人妖一区二区| 亚洲色大成永久WW网站| 91中文字幕一区二区| 日本一区二区三区黄色网| 西西人体44www大胆无码| 亚洲日韩AV秘 无码一区二区| 欧美高清精品一区二区| 无码无需播放器av网站| 国产AV福利第一精品| 二区三区亚洲精品国产| 国产一区二区午夜福利久久|