最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Golang中的錯(cuò)誤處理的示例詳解

 更新時(shí)間:2022年11月02日 14:50:55   作者:liuyuede123  
這篇文章主要為大家詳細(xì)介紹了Golang中的錯(cuò)誤處理的相關(guān)資料,文章中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Golang有一定幫助,需要的可以參考一下

1、panic

當(dāng)我們執(zhí)行panic的時(shí)候會(huì)結(jié)束下面的流程:

package main

import "fmt"

func main() {
	fmt.Println("hello")
	panic("stop")
	fmt.Println("world")
}

輸出:

go run 9.go 
hello
panic: stop

但是panic也是可以捕獲的,我們可以使用defer和recover實(shí)現(xiàn):

package main

import "fmt"

func main() {

	defer func() {
		if r := recover(); r != nil {
			fmt.Println("recover: ", r)
		}
	}()

	fmt.Println("hello")
	panic("stop")
	fmt.Println("world")
}

輸出:

go run 9.go
hello
recover:  stop

那什么時(shí)候適合panic呢?在 Go 中,panic 用于表示真正的異常,例如程序錯(cuò)誤。我們經(jīng)常會(huì)在一些內(nèi)置包里面看到panic的身影。

比如strings.Repeat重復(fù)返回一個(gè)由字符串 s 的計(jì)數(shù)副本組成的新字符串:

func Repeat(s string, count int) string {
	if count == 0 {
		return ""
	}

	// 
	if count < 0 {
		panic("strings: negative Repeat count")
	} else if len(s)*count/count != len(s) {
		panic("strings: Repeat count causes overflow")
	}

	...
}

我們可以看到當(dāng)重復(fù)的次數(shù)小于0或者重復(fù)count次之后s的長(zhǎng)度溢出,程序會(huì)直接panic,而不是返回錯(cuò)誤。這時(shí)因?yàn)閟trings包限制了error的使用,所以在程序錯(cuò)誤時(shí)會(huì)直接panic。

還有一個(gè)例子是關(guān)于正則表達(dá)式的例子:

package main

import (
	"fmt"
	"regexp"
)

func main() {
	pattern := "a[a-z]b*" // 1
	compile, err := regexp.Compile(pattern) // 2
	if err != nil { // 2
		fmt.Println("compile err: ", err)
		return
	}
  // 3
	allString := compile.FindAllString("acbcdadb", 3)
	fmt.Println(allString)

}
  • 編寫一個(gè)正則表達(dá)式
  • 調(diào)用Compile,解析正則表達(dá)式,如果成功,返回用于匹配文本的 Regexp 對(duì)象。否則返回錯(cuò)誤
  • 利用正則,在輸入的字符串中,獲取所有的匹配字符

可以看到如果上面正則解析失敗是可以繼續(xù)往下執(zhí)行的,但是regexp包中還有另外一個(gè)方法MustCompile:

func MustCompile(str string) *Regexp {
	regexp, err := Compile(str)
	if err != nil {
		panic(`regexp: Compile(` + quote(str) + `): ` + err.Error())
	}
	return regexp
}

這個(gè)方法說明正則的解析是強(qiáng)依賴的,如果解析錯(cuò)誤,直接panic結(jié)束程序。用戶可以根據(jù)實(shí)際情況選擇。

但是實(shí)際開發(fā)中我們還是要謹(jǐn)慎使用panic,因?yàn)樗鼤?huì)使程序結(jié)束運(yùn)行(除非我們調(diào)用defer recover)

2、包裝錯(cuò)誤

錯(cuò)誤包裝是將錯(cuò)誤包裝或者打包在一個(gè)包裝容器中,這樣的話我們就可以追溯到源錯(cuò)誤。錯(cuò)誤包裝的主要作用就是:

  • 為錯(cuò)誤添加上下文
  • 將錯(cuò)誤標(biāo)記為特定類型的錯(cuò)誤

我們可以看一個(gè)訪問數(shù)據(jù)庫的例子:

package main

import (
	"fmt"
	"github.com/pkg/errors"
)

type Courseware struct {
	Id int64
	Code string
	Name string
}

func getCourseware(id int64) (*Courseware, error) {
	courseware, err := getFromDB(id)
	if err != nil {
		return nil, errors.Wrap(err, "六月的想訪問這個(gè)課件") // 2
	}
	return courseware, nil
}

func getFromDB(id int64) (*Courseware, error) {
	return nil, errors.New("permission denied") // 1
}

func main() {
	_, err := getCourseware(11)
	if err != nil {
		fmt.Println(err)
	}
}
  • 訪問數(shù)據(jù)庫時(shí)我們返回了原始的錯(cuò)誤信息
  • 到上層我們添加了一些自定義的上下文信息

輸出:

go run 9.go
六月的想訪問這個(gè)課件: permission denied

當(dāng)然我們也可以將錯(cuò)誤包裝成我們自定義類型的錯(cuò)誤,我們稍微修改下上面的例子:

package main

import (
	"fmt"
	"github.com/pkg/errors"
)

type Courseware struct {
	Id int64
	Code string
	Name string
}

// 1
type ForbiddenError struct {
	Err error
}

// 2
func (e *ForbiddenError) Error() string {
	return "Forbidden: " + e.Err.Error()
}

func getCourseware(id int64) (*Courseware, error) {
	courseware, err := getFromDB(id)
	if err != nil {
		return nil, &ForbiddenError{err} // 4
	}
	return courseware, nil
}

func getFromDB(id int64) (*Courseware, error) {
	return nil, errors.New("permission denied") // 3
}

func main() {
	_, err := getCourseware(11)
	if err != nil {
		fmt.Println(err)
	}
}
  • 首先我們自定義了ForbiddenError的錯(cuò)誤類型
  • 我們實(shí)現(xiàn)了error接口
  • 訪問數(shù)據(jù)庫拋出原始錯(cuò)誤
  • 上層返回ForbiddenError類型的錯(cuò)誤

輸出:

go run 9.go
Forbidden: permission denied

當(dāng)然我們也可以不用創(chuàng)建自定義錯(cuò)誤的類型,去包裝錯(cuò)誤添加上下文:

package main

import (
	"fmt"
	"github.com/pkg/errors"
)

type Courseware struct {
	Id int64
	Code string
	Name string
}


func getCourseware(id int64) (*Courseware, error) {
	courseware, err := getFromDB(id)
	if err != nil {
		return nil, fmt.Errorf("another wrap err: %w", err) // 1
	}
	return courseware, nil
}

func getFromDB(id int64) (*Courseware, error) {
	return nil, errors.New("permission denied")
}

func main() {
	_, err := getCourseware(11)
	if err != nil {
		fmt.Println(err)
	}
}

使用%w包裝錯(cuò)誤

使用這的好處是我們可以追溯到源錯(cuò)誤,從而方便我們做一些特殊的處理。

還有一種方式是使用:

return nil, fmt.Errorf("another wrap err: %v", err)

%v的方式不會(huì)包裝錯(cuò)誤,所以無法追溯到源錯(cuò)誤,但往往有時(shí)候我們會(huì)選擇這種方式,而不用%w的方式。%w的方式雖然能包裝源錯(cuò)誤,但往往我們會(huì)通過源錯(cuò)誤去做一些處理,假如源錯(cuò)誤被修改,那包裝這個(gè)源錯(cuò)誤的相關(guān)錯(cuò)誤都需要做響應(yīng)變化。

3、錯(cuò)誤類型判斷

我們擴(kuò)展一下上面查詢課件的例子。現(xiàn)在我們有這樣的判斷,如果傳進(jìn)來的id不合法我們返回400錯(cuò)誤,如果查詢數(shù)據(jù)庫報(bào)錯(cuò)我們返回500錯(cuò)誤,我們可以像下面這樣寫:

package main

import (
	"fmt"
	"github.com/pkg/errors"
)

type Courseware struct {
	Id int64
	Code string
	Name string
}

type ForbiddenError struct {
	Err error
}

func (e *ForbiddenError) Error() string {
	return "Forbidden: " + e.Err.Error()
}

func getCourseware(id int64) (*Courseware, error) {
	if id <= 0 {
		return nil, fmt.Errorf("invalid id: %d", id)
	}
	courseware, err := getFromDB(id)
	if err != nil {
		return nil, &ForbiddenError{err}
	}
	return courseware, nil
}

func getFromDB(id int64) (*Courseware, error) {
	return nil, errors.New("permission denied")
}

func main() {
	_, err := getCourseware(500) // 我們可以修改這里的id看下打印的結(jié)構(gòu)
	if err != nil {
		switch err := err.(type) {
		case *ForbiddenError:
			fmt.Println("500 err: ", err)
		default:
			fmt.Println("400 err: ", err)
		}
	}
}

輸出:

go run 9.go
500 err:  Forbidden: permission denied

這樣看起來好像也沒什么問題,現(xiàn)在我們稍微修改下代碼,把上面ForbiddenError包裝一下:

package main

import (
	"fmt"
	"github.com/pkg/errors"
)

type Courseware struct {
	Id int64
	Code string
	Name string
}

type ForbiddenError struct {
	Err error
}

func (e *ForbiddenError) Error() string {
	return "Forbidden: " + e.Err.Error()
}

func getCourseware(id int64) (*Courseware, error) {
	if id <= 0 {
		return nil, fmt.Errorf("invalid id: %d", id)
	}
	courseware, err := getFromDB(id)
	if err != nil {
		return nil, fmt.Errorf("wrap err: %w", &ForbiddenError{err}) // 這里包裝了一層錯(cuò)誤
	}
	return courseware, nil
}

func getFromDB(id int64) (*Courseware, error) {
	return nil, errors.New("permission denied")
}

func main() {
	_, err := getCourseware(500)
	if err != nil {
		switch err := err.(type) {
		case *ForbiddenError:
			fmt.Println("500 err: ", err)
		default:
			fmt.Println("400 err: ", err)
		}
	}
}

輸出:

go run 9.go
400 err:  wrap err: Forbidden: permission denied

可以看到我們的Forbidden錯(cuò)誤進(jìn)到了400里面,這并不是我們想要的結(jié)果。之所以會(huì)這樣,是因?yàn)樵贔orbiddenError的外面又包裝了一層Error錯(cuò)誤,使用類型斷言的時(shí)候判斷出來的是Error錯(cuò)誤,所以進(jìn)到了400分支。

這里我們可以使用errors.As方法,它會(huì)遞歸調(diào)用Unwrap方法,找到錯(cuò)誤鏈中第一個(gè)與target匹配的方法:

package main

import (
	"fmt"
	"github.com/pkg/errors"
)

type Courseware struct {
	Id int64
	Code string
	Name string
}

type ForbiddenError struct {
	Err error
}

func (e *ForbiddenError) Error() string {
	return "Forbidden: " + e.Err.Error()
}

func getCourseware(id int64) (*Courseware, error) {
	if id <= 0 {
		return nil, fmt.Errorf("invalid id: %d", id)
	}
	courseware, err := getFromDB(id)
	if err != nil {
		return nil, fmt.Errorf("wrap err: %w", &ForbiddenError{err})
	}
	return courseware, nil
}

func getFromDB(id int64) (*Courseware, error) {
	return nil, errors.New("permission denied")
}

func main() {
	_, err := getCourseware(500)
	if err != nil {
		var f *ForbiddenError // 這里實(shí)現(xiàn)了*ForbiddenError接口,不然會(huì)panic
		if errors.As(err, &f) { // 找到匹配的錯(cuò)誤
			fmt.Println("500 err: ", err)
		} else {
			fmt.Println("400 err: ", err)
		}
	}
}

輸出:

go run 9.go
500 err:  wrap err: Forbidden: permission denied

4、錯(cuò)誤值判斷

在代碼中或者mysql庫或者io庫中我們經(jīng)常會(huì)看到這樣的全局錯(cuò)誤:

var ErrCourseware = errors.New("courseware")

這種錯(cuò)誤我們稱之為哨兵錯(cuò)誤。一般數(shù)據(jù)庫沒查到ErrNoRows或者io讀到了EOF錯(cuò)誤,這些特定的錯(cuò)誤可以幫助我們做一些特殊的處理。

一般我們會(huì)直接用==號(hào)判斷錯(cuò)誤值,但是就像上面的如果錯(cuò)誤被包裝哪我們就不好去判斷了。好在errors包中提供了errors.Is方法,通過遞歸調(diào)用Unwrap判斷錯(cuò)誤鏈中是否與目標(biāo)錯(cuò)誤相匹配的錯(cuò)誤值:

if err != nil {
    if errors.Is(err, ErrCourseware) {
        // ...
    } else {
        // ...
    }
}

到此這篇關(guān)于Golang中的錯(cuò)誤處理的示例詳解的文章就介紹到這了,更多相關(guān)Golang錯(cuò)誤處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang之sync.Pool使用詳解

    Golang之sync.Pool使用詳解

    這篇文章主要介紹了Golang之sync.Pool使用詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • 夯實(shí)Golang基礎(chǔ)之?dāng)?shù)據(jù)類型梳理匯總

    夯實(shí)Golang基礎(chǔ)之?dāng)?shù)據(jù)類型梳理匯總

    這篇文章主要8為大家介紹了夯實(shí)Golang基礎(chǔ)之?dāng)?shù)據(jù)類型梳理匯總,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2023-10-10
  • Go 實(shí)現(xiàn)英尺和米的簡(jiǎn)單單位換算方式

    Go 實(shí)現(xiàn)英尺和米的簡(jiǎn)單單位換算方式

    這篇文章主要介紹了Go 實(shí)現(xiàn)英尺和米的簡(jiǎn)單單位換算方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Go語言中database/sql的用法介紹

    Go語言中database/sql的用法介紹

    Go語言中的database/sql包定義了對(duì)數(shù)據(jù)庫的一系列操作,database/sql/driver包定義了應(yīng)被數(shù)據(jù)庫驅(qū)動(dòng)實(shí)現(xiàn)的接口,這些接口會(huì)被sql包使用,本文將詳細(xì)給大家介紹Go的database/sql的使用方法,需要的朋友可以參考下
    2023-05-05
  • go語言實(shí)現(xiàn)將重要數(shù)據(jù)寫入圖片中

    go語言實(shí)現(xiàn)將重要數(shù)據(jù)寫入圖片中

    本文給大家分享的是go語言實(shí)現(xiàn)將數(shù)據(jù)的二進(jìn)制形式寫入圖像紅色通道數(shù)據(jù)二進(jìn)制的低位,從而實(shí)現(xiàn)將重要數(shù)據(jù)隱藏,有需要的小伙伴參考下吧。
    2015-03-03
  • go語言遍歷文件夾示例

    go語言遍歷文件夾示例

    這篇文章主要介紹了go語言遍歷文件夾示例,本文直接給出實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2015-03-03
  • 詳解Golang?Map中的key為什么是無序的

    詳解Golang?Map中的key為什么是無序的

    本文主要介紹了Golang?Map中的key為什么是無序的,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • go語言簡(jiǎn)單的處理http請(qǐng)求的函數(shù)實(shí)例

    go語言簡(jiǎn)單的處理http請(qǐng)求的函數(shù)實(shí)例

    這篇文章主要介紹了go語言簡(jiǎn)單的處理http請(qǐng)求的函數(shù),實(shí)例分析了Go語言處理http請(qǐng)求的技巧,需要的朋友可以參考下
    2015-03-03
  • golang 切片的三種使用方式及區(qū)別的說明

    golang 切片的三種使用方式及區(qū)別的說明

    這篇文章主要介紹了golang 切片的三種使用方式及區(qū)別的說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 使用go的interface案例實(shí)現(xiàn)多態(tài)范式操作

    使用go的interface案例實(shí)現(xiàn)多態(tài)范式操作

    這篇文章主要介紹了使用go的interface案例實(shí)現(xiàn)多態(tài)范式操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12

最新評(píng)論

荥阳市| 称多县| 隆安县| 安溪县| 晋宁县| 泰来县| 宜都市| 青川县| 绩溪县| 兴仁县| 海兴县| 汾西县| 永清县| 乐都县| 定远县| 镇平县| 广水市| 东阳市| 体育| 翁源县| 兰西县| 昭觉县| 巩义市| 栾城县| 汉中市| 郁南县| 苍溪县| 四子王旗| 塘沽区| 隆回县| 芮城县| 高州市| 嘉峪关市| 凤山县| 房产| 九江市| 西乌珠穆沁旗| 蕲春县| 旬阳县| 微博| 定陶县|