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

Golang標(biāo)準(zhǔn)庫(kù)之errors包應(yīng)用方式

 更新時(shí)間:2024年10月19日 14:55:32   作者:一只coding豬  
Go語(yǔ)言的errors包提供了基礎(chǔ)的錯(cuò)誤處理能力,允許通過(guò)errors.New創(chuàng)建自定義error對(duì)象,error在Go中是一個(gè)接口,通過(guò)實(shí)現(xiàn)Error方法來(lái)定義錯(cuò)誤文本,對(duì)錯(cuò)誤的比較通常基于對(duì)象地址,而非文本內(nèi)容,因此即使兩個(gè)錯(cuò)誤文本相同

一. errors的基本應(yīng)用

errors包是一個(gè)比較簡(jiǎn)單的包,包括常見(jiàn)的errors.New創(chuàng)建一個(gè)error對(duì)象,或通過(guò)error.Error方法獲取error中的文本內(nèi)容,本質(zhì)上在builtin類型中,error被定義為一個(gè)interface,這個(gè)類型只包含一個(gè)Error方法,返回字符串形式的錯(cuò)誤內(nèi)容。

應(yīng)用代碼很簡(jiǎn)單:

// 示例代碼
func Oops() error {
	return errors.New("iam an error")
}

func Print() {
	err := Oops()
	fmt.Println("oops, we go an error,", err.Error())
}

通過(guò)errors.New方法,可以創(chuàng)建一個(gè)error對(duì)象,在標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)中,對(duì)應(yīng)了一個(gè)叫errorString的實(shí)體類型,是對(duì)error接口的最基本實(shí)現(xiàn)。

二. 錯(cuò)誤類型的比較

代碼中經(jīng)常會(huì)出現(xiàn)err == nil 或者err == ErrNotExist之類的判斷,對(duì)于error類型,由于其是interface類型,實(shí)際比較的是interface接口對(duì)象實(shí)體的地址。

也就是說(shuō),重復(fù)的new兩個(gè)文本內(nèi)容一樣的error對(duì)象,這兩個(gè)對(duì)象并不相等,因?yàn)楸容^的是這兩個(gè)對(duì)象的地址。這是完全不同的兩個(gè)對(duì)象

// 展示了error比較代碼
if errors.New("hello error") == errors.New("hello error") { // false
}
errhello := errors.New("hello error")
if errhello == errhello { // true
}

在通常的場(chǎng)景中,能掌握errors.New()、error.Error()以及error對(duì)象的比較,就能應(yīng)付大多數(shù)場(chǎng)景了,但是在大型系統(tǒng)中,內(nèi)置的error類型很難滿足需要,所以下面要講的是對(duì)error的擴(kuò)展。

三. error的擴(kuò)展

3.1 自定義error

go允許函數(shù)具有多返回值,但通常你不會(huì)想寫(xiě)太多的返回值在函數(shù)定義上(looks ugly),而標(biāo)準(zhǔn)庫(kù)內(nèi)置的errorString類型由于只能表達(dá)字符串錯(cuò)誤信息顯然受限。所以,可以通過(guò)實(shí)現(xiàn)error接口的方式,來(lái)擴(kuò)展錯(cuò)誤返回

// 自定義error類型
type EasyError struct {
	Msg  string	// 錯(cuò)誤文本信息
	Code int64	// 錯(cuò)誤碼
}

func (me *EasyError) Error() string {
	// 當(dāng)然,你也可以自定義返回的string,比如
	// return fmt.Sprintf("code %d, msg %s", me.Code, me.Msg)
	return me.Msg
}

// Easy實(shí)現(xiàn)了error接口,所以可以在Oops中返回
func DoSomething() error {
	return &EasyError{"easy error", 1}
}

// 業(yè)務(wù)應(yīng)用
func DoBusiness() {
	err := DoSomething()
	e,ok := err.(EasyError)
	if ok {
		fmt.Printf("code %d, msg %s\n", e.Code, e.Msg)
	}
}

現(xiàn)在在自定義的錯(cuò)誤類型中塞入了錯(cuò)誤碼信息。隨著業(yè)務(wù)代碼調(diào)用層層深入,當(dāng)最內(nèi)層的操作(比如數(shù)據(jù)庫(kù)操作)發(fā)生錯(cuò)誤時(shí),我們希望能在業(yè)務(wù)調(diào)用鏈上每一層都攜帶錯(cuò)誤信息,就像遞歸調(diào)用一樣,這時(shí)可以用到標(biāo)準(zhǔn)庫(kù)的Unwrap方法

3.2 Unwrap與Nested error

一旦你的自定義error實(shí)現(xiàn)類型定義了Unwrap方法,那么它就具有了嵌套的能力,其函數(shù)原型定義如下:

// 標(biāo)準(zhǔn)庫(kù)Unwrap方法,傳入一個(gè)error對(duì)象,返回其內(nèi)嵌的error
func Unwrap(err error) error

// 自定義Unwrap方法
func (me *EasyError) Unwrap() error {
	// ... 
}

雖然error接口沒(méi)有定義Unwrap方法,但是標(biāo)準(zhǔn)庫(kù)的Unwrap方法中會(huì)通過(guò)反射隱式調(diào)用自定義類型的Unwrap方法,這也是業(yè)務(wù)實(shí)現(xiàn)自定義嵌套的途徑。我們給EasyError增加一個(gè)error成員,表示包含的下一級(jí)error

// 
type EasyError struct {
	Msg  string	// 錯(cuò)誤文字信息
	Code int64	// 錯(cuò)誤碼
	Nest error 	// 嵌套的錯(cuò)誤
}

func (me *EasyError) Unwrap() error {
	return me.Nest
}

func DoSomething1() error {
	// ...
	err := DoSomething2()
	if err != nil {
		return &EasyError{"from DoSomething1", 1, err}
	}

	return nil
}

func DoSomething2() error {
	// ...
	err := DoSomething3()
	if err != nil {
		return &EasyError{"from DoSomething2", 2, err}
	}

	return nil
}

func DoSomething3() error {
	// ...

	return &EasyError{"from DoSomething3", 3, nil}
}
// 可以很清楚的看到調(diào)用鏈上產(chǎn)生的錯(cuò)誤信息
// Output:
// 	code 1, msg from DoSomething1
// 	code 2, msg from DoSomething2
// 	code 3, msg from DoSomething3
func main() {
	err := DoSomething1()
	for err != nil {
		e := err.(*EasyError)
		fmt.Printf("code %d, msg %s\n", e.Code, e.Msg)
		err = errors.Unwrap(err)		// errors.Unwrap中調(diào)用EasyError的Unwrap返回子error
	}
}

輸出如下

$ ./sample
code 1, msg from DoSomething1
code 2, msg from DoSomething2
code 3, msg from DoSomething3

這樣就可以在深入的調(diào)用鏈中,通過(guò)嵌套的方式,將調(diào)用路徑中的錯(cuò)誤信息,攜帶至調(diào)用棧的棧底。

對(duì)于不同模塊,返回的錯(cuò)誤信息大不相同,比如網(wǎng)絡(luò)通信模塊期望錯(cuò)誤信息攜帶http狀態(tài)碼,而數(shù)據(jù)持久層期望返回sql或redis commend,隨著模塊化的職能劃分,每個(gè)子模塊可能會(huì)定義自己的自定義error類型,這時(shí)在業(yè)務(wù)上去區(qū)分不同類別的錯(cuò)誤,就可以使用Is方法

3.3 errors.Is方法與錯(cuò)誤分類

以網(wǎng)絡(luò)錯(cuò)誤和數(shù)據(jù)庫(kù)錯(cuò)誤為例,分別定義兩種實(shí)現(xiàn)error接口的結(jié)構(gòu)NetworkError和DatabaseError。

// 網(wǎng)絡(luò)接口返回的錯(cuò)誤類型
type NetworkError struct {
	Code   int	  // 10000 - 19999
	Msg    string // 文本信息
	Status int    // http狀態(tài)碼
}

// 數(shù)據(jù)庫(kù)模塊接口返回的錯(cuò)誤類型
type DatabaseError struct {
	Code int	// 20000 - 29999
	Msg  string // 文本錯(cuò)誤信息
	Sql  string // sql string
}

NetworkError與DatabaseError都實(shí)現(xiàn)了Error方法和Unwrap方法,代碼里就不重復(fù)寫(xiě)了。錯(cuò)誤類型的劃分,導(dǎo)致上層業(yè)務(wù)對(duì)error的處理產(chǎn)生變化:業(yè)務(wù)層需要知道發(fā)生了什么,才能給用戶提供恰當(dāng)?shù)奶崾?,但是又不希望過(guò)分詳細(xì),比如用戶期望看到的是“數(shù)據(jù)訪問(wèn)異常”、“請(qǐng)檢查網(wǎng)絡(luò)狀態(tài)”,而不希望用戶看到“unknown column space in field list…”、“request timeout…”之類的技術(shù)性錯(cuò)誤信息。此時(shí)Is方法就派上用場(chǎng)了。

現(xiàn)在我們?yōu)榫W(wǎng)絡(luò)或數(shù)據(jù)庫(kù)錯(cuò)誤都增加一個(gè)Code錯(cuò)誤碼,并且人為對(duì)錯(cuò)誤碼區(qū)間進(jìn)行劃分,[10000,20000)表示網(wǎng)絡(luò)錯(cuò)誤,[20000,30000)表示數(shù)據(jù)庫(kù)錯(cuò)誤,我們期望在業(yè)務(wù)層能夠知道錯(cuò)誤碼中是否包含網(wǎng)絡(luò)錯(cuò)誤或數(shù)據(jù)訪問(wèn)錯(cuò)誤,還需要為兩種錯(cuò)誤類型添加Is方法:

var(
	// 將10000和20000預(yù)留,用于在Is方法中判斷錯(cuò)誤碼區(qū)間
	ErrNetwork  = &NetworkError{EasyError{"", 10000, nil}, 0}
	ErrDatabase = &DatabaseError{EasyError{"", 20000, nil}, ""}
)

func (ne NetworkError) Is(e error) bool {
	err, ok := e.(*NetworkError)
	if ok {
		start := err.Code / 10000
		return ne.Code >= 10000 && ne.Code < (start+1)*10000
	}
	return false
}

func (de DatabaseError) Is(e error) bool {
	err, ok := e.(*DatabaseError)
	if ok {
		start := err.Code / 10000
		return de.Code >= 10000 && de.Code < (start+1)*10000
	}
	return false
}

與Unwrap類似,Is方法也是被errors.Is方法隱式調(diào)用的,來(lái)看一下業(yè)務(wù)代碼

func DoNetwork() error {
	// ...
	return &NetworkError{EasyError{"", 10001, nil}, 404}
}

func DoDatabase() error {
	// ...
	return &DatabaseError{EasyError{"", 20003, nil}, "select 1"}
}

func DoSomething() error {
	if err := DoNetwork(); err != nil {
		return err
	}
	if err := DoDatabase(); err != nil {
		return err
	}
	return nil
}

func DoBusiness() error {
	err := DoSomething()
	if err != nil {
		if errors.Is(err, ErrNetworks) {
			fmt.Println("網(wǎng)絡(luò)異常")
		} else if errors.Is(err, ErrDatabases) {
			fmt.Println("數(shù)據(jù)訪問(wèn)異常")
		}
	} else {
		fmt.Println("everything is ok")
	}
	return nil
}

執(zhí)行DoBusiness,輸出如下:

$ ./sample
網(wǎng)絡(luò)異常

通過(guò)Is方法,可以將一批錯(cuò)誤信息歸類,對(duì)應(yīng)用隱藏相關(guān)信息,畢竟大部分時(shí)候,我們不希望用戶直接看到出錯(cuò)的sql語(yǔ)句。

3.4 errors.As方法與錯(cuò)誤信息讀取

現(xiàn)在通過(guò)Is實(shí)現(xiàn)了分類,可以判斷一個(gè)錯(cuò)誤是否是某個(gè)類型,但是更進(jìn)一步,如果我們想得到不同錯(cuò)誤類型的詳細(xì)信息呢?業(yè)務(wù)層拿到返回的error,就不得不通過(guò)層層Unwrap和類型斷言來(lái)獲取調(diào)用鏈中的深層錯(cuò)誤信息。所以errors包提供了As方法,在Unwrap的基礎(chǔ)上,直接獲取error接口中,實(shí)際是error鏈中指定類型的錯(cuò)誤。

所以在DatabaseError的基礎(chǔ)上,再定義一個(gè)RedisError類型,作為封裝redis訪問(wèn)異常的類型

// Redis模塊接口返回的錯(cuò)誤類型
type RedisError struct {
	EasyError
	Command string // redis commend
	Address string // redis instance address
}

func (re *RedisError) Error() string {
	return re.Msg
}

在業(yè)務(wù)層,嘗試讀取數(shù)據(jù)庫(kù)和redis錯(cuò)誤的詳細(xì)信息

func DoDatabase() error {
	// ...
	return &DatabaseError{EasyError{"", 20003, nil}, "select 1"}
}

func DoRedis() error {
	// ...
	return &RedisError{EasyError{"", 30010, nil}, "set hello 1", "127.0.0.1:6379"}
}

func DoDataWork() error {
	if err := DoRedis(); err != nil {
		return err
	}
	if err := DoDatabase(); err != nil {
		return err
	}
	return nil
}

// 執(zhí)行業(yè)務(wù)代碼
func DoBusiness() {
	err := DoDataWork()
	if err != nil {
		if rediserr := (*RedisError)(nil); errors.As(err, &rediserr) {
			fmt.Printf("Redis exception, commend : %s, instance : %s\n", rediserr.Command, rediserr.Address)
		} else if mysqlerr := (*DatabaseError)(nil); errors.As(err, &mysqlerr) {
			fmt.Printf("Mysql exception, sql : %s\n", mysqlerr.Sql)
		}
	} else {
		fmt.Println("everything is ok")
	}
}

運(yùn)行DoBusiness,輸出如下

$ ./sample
Redis exception, commend : set hello 1, instance : 127.0.0.1:6379

conclusion

  • error是interface類型,可以實(shí)現(xiàn)自定義的error類型
  • error支持鏈?zhǔn)降慕M織形式,通過(guò)自定義Unwrap實(shí)現(xiàn)對(duì)error鏈的遍歷
  • errors.Is用于判定error是否屬于某類錯(cuò)誤,歸類方式可以在自定義error的Is方法中實(shí)現(xiàn)
  • errors.As同樣可以用于判斷error是否屬于某個(gè)錯(cuò)誤,避免了顯式的斷言處理,并同時(shí)返回使用該類型錯(cuò)誤表達(dá)的錯(cuò)誤信息詳情
  • 無(wú)論是Is還是As方法,都會(huì)嘗試調(diào)用Unwrap方法遞歸地查找錯(cuò)誤,所以如果帶有Nesty的錯(cuò)誤,務(wù)必要實(shí)現(xiàn)Unwrap方法才可以正確匹配

通過(guò)這些手段,可以在不侵入業(yè)務(wù)接口的情況下,豐富錯(cuò)誤處理,這就是errors包帶來(lái)的便利。

總結(jié)

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

相關(guān)文章

  • Go語(yǔ)言reflect包的反射機(jī)制基本用法示例

    Go語(yǔ)言reflect包的反射機(jī)制基本用法示例

    反射在處理接口和類型斷言、開(kāi)發(fā)通用功能或者設(shè)計(jì)框架時(shí)尤為重要,本文將深入探索 Go 語(yǔ)言中的反射機(jī)制,通過(guò)具體的示例展示如何使用?reflect?包,讓你能夠在 Go 項(xiàng)目中有效地利用這一強(qiáng)大的工具
    2023-11-11
  • GOLang?IO接口與工具使用方法講解

    GOLang?IO接口與工具使用方法講解

    這篇文章主要介紹了GOLang?IO接口與工具使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2023-03-03
  • golang如何使用gos7讀取S7200Smart數(shù)據(jù)

    golang如何使用gos7讀取S7200Smart數(shù)據(jù)

    文章介紹了如何使用Golang語(yǔ)言的Gos7工具庫(kù)讀取西門(mén)子S7200Smart系列PLC的數(shù)據(jù),通過(guò)指定數(shù)據(jù)塊號(hào)、起始字節(jié)偏移量和數(shù)據(jù)長(zhǎng)度,可以精確讀取所需的數(shù)據(jù),感興趣的朋友跟隨小編一起看看吧
    2024-12-12
  • go編譯標(biāo)簽build?tag注釋里語(yǔ)法詳解

    go編譯標(biāo)簽build?tag注釋里語(yǔ)法詳解

    這篇文章主要為大家介紹了go編譯標(biāo)簽build?tag注釋里語(yǔ)法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Go高效率開(kāi)發(fā)Web參數(shù)校驗(yàn)三種方式實(shí)例

    Go高效率開(kāi)發(fā)Web參數(shù)校驗(yàn)三種方式實(shí)例

    這篇文章主要介紹了Go高效率開(kāi)發(fā)Web參數(shù)校驗(yàn)三種方式實(shí)例,需要的朋友可以參考下
    2022-11-11
  • Go語(yǔ)言的文件操作代碼匯總

    Go語(yǔ)言的文件操作代碼匯總

    本文給大家匯總介紹了go語(yǔ)言中的文件操作的代碼,包括文件的讀寫(xiě),文件的新建打開(kāi)和刪除等,希望對(duì)大家學(xué)習(xí)go語(yǔ)言能夠有所幫助
    2018-10-10
  • sublime text3解決Gosublime無(wú)法自動(dòng)補(bǔ)全代碼的問(wèn)題

    sublime text3解決Gosublime無(wú)法自動(dòng)補(bǔ)全代碼的問(wèn)題

    本文主要介紹了sublime text3解決Gosublime無(wú)法自動(dòng)補(bǔ)全代碼的問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • golang?中?recover()的使用方法

    golang?中?recover()的使用方法

    這篇文章主要介紹了Guam與golang??recover()的使用方法,Recover?是一個(gè)Go語(yǔ)言的內(nèi)建函數(shù),可以讓進(jìn)入宕機(jī)流程中的?goroutine?恢復(fù)過(guò)來(lái),下文更多相關(guān)資料需要的小伙伴可以參考一下
    2022-04-04
  • 解析GOROOT、GOPATH、Go-Modules-三者的關(guān)系

    解析GOROOT、GOPATH、Go-Modules-三者的關(guān)系

    這篇文章主要介紹了解析GOROOT、GOPATH、Go-Modules-三者的關(guān)系,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Go實(shí)現(xiàn)快速生成固定長(zhǎng)度的隨機(jī)字符串

    Go實(shí)現(xiàn)快速生成固定長(zhǎng)度的隨機(jī)字符串

    這篇文章主要為大家詳細(xì)介紹了怎樣在Go中簡(jiǎn)單快速地生成固定長(zhǎng)度的隨機(jī)字符串,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以學(xué)習(xí)一下
    2022-10-10

最新評(píng)論