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

詳解Golang中創(chuàng)建error的方式總結(jié)與應(yīng)用場(chǎng)景

 更新時(shí)間:2023年07月05日 11:36:13   作者:Go學(xué)堂  
Golang中創(chuàng)建error的方式包括errors.New、fmt.Errorf、自定義實(shí)現(xiàn)了error接口的類型等,本文主要為大家介紹了這些方式的具體應(yīng)用場(chǎng)景,需要的可以參考一下

01 什么是Error

在Go中,error是一種內(nèi)建的數(shù)據(jù)類型,被定義為一個(gè)接口,定義如下:

// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
	Error() string
}

由此可知,該接口只有一個(gè)返回字符串的Error函數(shù),所有的類型只要實(shí)現(xiàn)了該函數(shù),就創(chuàng)建了一個(gè)錯(cuò)誤類型。

02 創(chuàng)建error的方式

創(chuàng)建error的方式包括errors.New、fmt.Errorf、自定義實(shí)現(xiàn)了error接口的類型等。

2.1 通過(guò)errors.New方法創(chuàng)建

通過(guò)該方法創(chuàng)建的錯(cuò)誤一般是可預(yù)知的錯(cuò)誤。簡(jiǎn)單來(lái)說(shuō)就是調(diào)用者通過(guò)該錯(cuò)誤信息就能明確的知道哪里出錯(cuò)了,而不需要再額外的添加其他上下文信息,我們?cè)谙旅娴氖纠性敿?xì)說(shuō)明。

err := errors.New("this is error")

我們看New方法的實(shí)現(xiàn)可知,實(shí)際上是返回了一個(gè)errorString結(jié)構(gòu)體,該結(jié)構(gòu)體包含了一個(gè)字符串屬性,并實(shí)現(xiàn)了Error方法。代碼如下:

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
}

error.New 使用場(chǎng)景1

通過(guò)errors.New函數(shù)創(chuàng)建局部變量或匿名變量,且不在調(diào)用函數(shù)中進(jìn)行值或類型判斷的處理,只打印或記錄錯(cuò)誤日志的場(chǎng)景。

使用示例1

以下代碼節(jié)選自源碼/src/net/http/request.go中解析PostForm的部分。當(dāng)請(qǐng)求中的Body為nil時(shí),返回的錯(cuò)誤信息是"missing form body"。該信息已明確的說(shuō)明錯(cuò)誤是因?yàn)檎?qǐng)求體為空造成的,所以不需要再額外的添加其他上下文信息。

func parsePostForm(r *Request) (vs url.Values, err error) {
	if r.Body == nil {
		err = errors.New("missing form body")
		return
	}
	ct := r.Header.Get("Content-Type")
	// 省略了后續(xù)的代碼...
	return
}

使用示例2

以下代碼節(jié)選自源碼/src/net/http/transport.go的部分,當(dāng)請(qǐng)求體中的url地址為nil返回的錯(cuò)誤:"http: nil Request.URL" ,說(shuō)明是請(qǐng)求中的URL字段為nil。以及當(dāng)Header為nil返回的錯(cuò)誤:"http:nil Request.Header",說(shuō)明請(qǐng)求體中的Header字段為nil。

func (t *Transport) roundTrip(req *Request) (*Response, error) {
	t.nextProtoOnce.Do(t.onceSetNextProtoDefaults)
	ctx := req.Context()
	trace := httptrace.ContextClientTrace(ctx)
	if req.URL == nil {
		req.closeBody()
		return nil, errors.New("http: nil Request.URL")
	}
	if req.Header == nil {
		req.closeBody()
		return nil, errors.New("http: nil Request.Header")
	}
	//省略后面的代碼...
}

error.New 使用場(chǎng)景2

將errors.New創(chuàng)建的錯(cuò)誤賦值給一個(gè)全局的變量,我們稱該變量為哨兵錯(cuò)誤,該哨兵錯(cuò)誤變量可以在被處理的時(shí)候使用 == 或 errors.Is來(lái)進(jìn)行值的比較。

使用示例

在源碼/src/io/io.go中定義的代表文件末尾的哨兵錯(cuò)誤變量EOF。

var EOF = errors.New("EOF")

在beego項(xiàng)目中,beego/core/utils/file.go文件中有這樣的應(yīng)用,當(dāng)讀取文件時(shí),遇到的錯(cuò)誤不是文件末尾的錯(cuò)誤則直接返回,如果遇到的是文件末尾的錯(cuò)誤,則中斷for循環(huán),說(shuō)明文件已經(jīng)讀完文件中的所有內(nèi)容了。如下:

func GrepFile(patten string, filename string) (lines []string, err error) {
	//省略前面的代碼...
	fd, err := os.Open(filename)
	if err != nil {
		return
	}
	reader := bufio.NewReader(fd)
	for {
		byteLine, isPrefix, er := reader.ReadLine()
		if er != nil && er != io.EOF {
			return nil, er
		}
		if er == io.EOF {
			break
		}
		//省略后面的代碼...
	}

2.2 通過(guò)fmt.Errorf方法創(chuàng)建

該方法也有兩種形式,一種是帶%w占位符的,一種是不帶%w占位符的方式。

使用場(chǎng)景1:不帶%w占位符

在創(chuàng)建錯(cuò)誤的時(shí)候,不能通過(guò)errors.New創(chuàng)建的字符串信息來(lái)描述錯(cuò)誤,而需要通過(guò)占位符添加更多的上下文信息,即動(dòng)態(tài)信息。

使用示例:不帶%w占位符

以下代碼節(jié)選自gorm/schema/relationship.go的部分代碼,當(dāng)外鍵不合法時(shí),通過(guò)fmt.Errorf("invalid foreign key:%s", foreignKey)返回帶具體外鍵的錯(cuò)誤。因?yàn)橥怄I值是在運(yùn)行時(shí)才能確定的。代碼如下:

func (schema *Schema) buildMany2ManyRelation(relation *Relationship, field *Field, many2many string) {
	//...
	if len(relation.foreignKeys) > 0 {
		ownForeignFields = []*Field{}
		for _, foreignKey := range relation.foreignKeys {
			if field := schema.LookUpField(foreignKey); field != nil {
				ownForeignFields = append(ownForeignFields, field)
			} else {
				schema.err = fmt.Errorf("invalid foreign key: %s", foreignKey)
				return
			}
		}
	}
	//...
}

使用場(chǎng)景2:帶%w占位符

在有些場(chǎng)景下,調(diào)用者需要知道原始錯(cuò)誤信息,這時(shí)就需要使用帶%w占位符的fmt.Errorf方式來(lái)創(chuàng)建錯(cuò)誤,使用這種方式,其實(shí)是形成了一個(gè)錯(cuò)誤鏈。

其用法如下:

filename := "abc.webp"
fmt.Errorf("%w:%s", errors.New("unsupported extension"), filename)

我們?cè)賮?lái)看下源代碼:

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
}

通過(guò)源碼可知,如果fmt.Errorf中包含%w占位符,創(chuàng)建的是一個(gè)wrapError結(jié)構(gòu)體類型的值。我們?cè)賮?lái)看下wrapError結(jié)構(gòu)體的定義:

type wrapError struct {
	msg string
	err error
}

字段err就是原始錯(cuò)誤,msg是經(jīng)過(guò)格式化之后的錯(cuò)誤信息。

使用示例:帶%w占位符

假設(shè)我們有一個(gè)從數(shù)據(jù)庫(kù)查詢合同的函數(shù),當(dāng)從數(shù)據(jù)庫(kù)中查詢到記錄為空時(shí),會(huì)返回一個(gè)sql.ErrNoRows錯(cuò)誤,我們用%w占位符來(lái)wrap該錯(cuò)誤,并返回給調(diào)用者。

const query = "..."
func (s Store) GetContract(name string) (Contract, error) {
	id := getID(name)
	rows, err := s.db.Query(query, id)
	if err != nil {
		if err == sql.ErrNoRows {
			return Contract{},
			fmt.Errorf("no contract found for %s: %w", name, err) 
		}
		// ...
	}
	// ...
}

好了,現(xiàn)在GetContract的調(diào)用者可以知道原始的錯(cuò)誤信息了。在調(diào)用者邏輯中我們可以使用errors.Is來(lái)判斷err中是否包含sql.ErrNoRows值了。我們看下調(diào)用者的代碼:

contract, err := store.GetContract("Raul Endymion")
if err != nil {
	if errors.Is(err, sql.ErrNoRows) { 
		// Do something specific
	}
}

2.3 自定義實(shí)現(xiàn)了error接口的結(jié)構(gòu)體

使用場(chǎng)景

這個(gè)是相對(duì)errors.New來(lái)說(shuō)的,errors.New適用于對(duì)可預(yù)知的錯(cuò)誤的定義。而當(dāng)發(fā)生了不可預(yù)知的錯(cuò)誤時(shí),就需要自定義錯(cuò)誤類型了。

使用示例

我們以go中/src/io/fs/fs.go文件中的源碼為例,來(lái)看下自定義錯(cuò)誤類型都需要包含哪些元素。

// PathError records an error and the operation and file path that caused it.
type PathError struct {
	Op   string
	Path string
	Err  error
}
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
func (e *PathError) Unwrap() error { return e.Err }

首先看結(jié)構(gòu)體,有一個(gè)error接口類型的Err,這個(gè)代表的是錯(cuò)誤源,因?yàn)楦鶕?jù)上面講解的,在錯(cuò)誤層層傳遞返回給調(diào)用者時(shí),我們需要追蹤每一層的原始錯(cuò)誤信息,所以需要該字段對(duì)error進(jìn)行wrap,形成錯(cuò)誤鏈。另外,有兩個(gè)字段Op和Path,分別代表是產(chǎn)生該錯(cuò)誤的操作和操作的路徑。這兩個(gè)字段就是所謂的未預(yù)料到的錯(cuò)誤:不確定是針對(duì)哪個(gè)路徑做了什么錯(cuò)誤引發(fā)了該錯(cuò)誤。

我們看下該錯(cuò)誤類型在代碼中的應(yīng)用。

應(yīng)用1:在go的文件src/embed/embed.go中的代碼,當(dāng)讀取某目錄時(shí)返回的一個(gè)PathError類型的錯(cuò)誤,代表讀取該目錄操作時(shí),因?yàn)槭且粋€(gè)目錄,所以不能直接讀取文件內(nèi)容。

func (d *openDir) Read([]byte) (int, error) {
	return 0, &fs.PathError{Op: "read", Path: d.f.name, Err: errors.New("is a directory")}
}

應(yīng)用2:在go的文件src/embed/embed.go中的代碼中,有文件讀取的函數(shù),當(dāng)offset小于0時(shí),返回了一個(gè)PathError,代表是在讀取該文件的時(shí)候,參數(shù)不正確。

func (f *openFile) Read(b []byte) (int, error) {
	if f.offset >= int64(len(f.f.data)) {
		return 0, io.EOF
	}
	if f.offset < 0 {
		return 0, &fs.PathError{Op: "read", Path: f.f.name, Err: fs.ErrInvalid}
	}
	n := copy(b, f.f.data[f.offset:])
	f.offset += int64(n)
	return n, nil
}

fs.ErrInvalid的定義如下

ErrInvalid    = errors.New("invalid argument")

由此可見(jiàn),PathError中的三個(gè)字段值都是不可預(yù)知的,都需要在程序運(yùn)行時(shí)才能具體決定的,所以這種場(chǎng)景時(shí),則需要自定義錯(cuò)誤類型。

另外,我們還注意到該自定義的類型中有Unwrap函數(shù)的實(shí)現(xiàn),該函數(shù)主要是為了配合errors.Is和errors.As使用的,因?yàn)檫@兩個(gè)函數(shù)在使用時(shí)是將錯(cuò)誤鏈層層解包一一比對(duì)的。

03 errors.Is和errors.As

根據(jù)上一節(jié)我們得到,通過(guò)%w占位符可以將錯(cuò)誤組織成一個(gè)錯(cuò)誤鏈。我們?cè)賮?lái)看看通過(guò)errors.Is和errors.As如何處理被wrap過(guò)的錯(cuò)誤鏈。

errors.Is函數(shù)就是來(lái)判斷錯(cuò)誤鏈中有沒(méi)有和指定的錯(cuò)誤值相等的錯(cuò)誤,相當(dāng)于 == 操作符。注意,這里是特定的錯(cuò)誤值,就像gorm中定義的ErrRecordNotFound這樣:

var ErrRecordNotFound = errors.New("record not found")

那么我們就可以這樣使用errors.Is:

errors.Is(err, ErrRecordNotFound)

errors.As函數(shù),該函數(shù)是用來(lái)檢查錯(cuò)誤鏈中的錯(cuò)誤是否有指定的錯(cuò)誤類型的。

如下代碼示例是節(jié)選自etcd項(xiàng)目etcd/server/embed/config_logging.go中的部分代碼,代表的是err鏈中有沒(méi)有能當(dāng)做json.SyntaxError類型的錯(cuò)誤的,如果能,則將err中的錯(cuò)誤值賦值到syntaxError變量上,代碼如下:

// setupLogRotation initializes log rotation for a single file path target.
func setupLogRotation(logOutputs []string, logRotateConfigJSON string) error {
	//...
	if err := json.Unmarshal([]byte(logRotateConfigJSON), &logRotationConfig); err != nil {
		var unmarshalTypeError *json.UnmarshalTypeError
		var syntaxError *json.SyntaxError
		switch {
		case errors.As(err, &syntaxError):
			return fmt.Errorf("improperly formatted log rotation config: %w", err)
		case errors.As(err, &unmarshalTypeError):
			return fmt.Errorf("invalid log rotation config: %w", err)
		}
	}
	zap.RegisterSink("rotate", func(u *url.URL) (zap.Sink, error) {
		logRotationConfig.Filename = u.Path[1:]
		return &logRotationConfig, nil
	})
	return nil
}

總結(jié)

本文從應(yīng)用場(chǎng)景的角度講解了各種創(chuàng)建錯(cuò)誤方式的實(shí)際應(yīng)用場(chǎng)景。示例中的代碼盡量的選自golang源碼或開源項(xiàng)目。同時(shí),每種的應(yīng)用場(chǎng)景并非絕對(duì)的,需要靈活應(yīng)用。希望本文對(duì)大家在實(shí)際使用中能夠有所幫助。

以上就是詳解Golang中創(chuàng)建error的方式總結(jié)與應(yīng)用的詳細(xì)內(nèi)容,更多關(guān)于Golang創(chuàng)建error的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • golang中使用mongo的方法介紹

    golang中使用mongo的方法介紹

    這篇文章主要給大家介紹了關(guān)于golang中使用mongo的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08
  • golang實(shí)現(xiàn)命令行程序的使用幫助功能

    golang實(shí)現(xiàn)命令行程序的使用幫助功能

    這篇文章介紹了golang實(shí)現(xiàn)命令行程序使用幫助的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • 在Go語(yǔ)言開發(fā)中實(shí)現(xiàn)高性能的分布式日志收集的方法

    在Go語(yǔ)言開發(fā)中實(shí)現(xiàn)高性能的分布式日志收集的方法

    本文介紹了在Go語(yǔ)言開發(fā)中實(shí)現(xiàn)高性能分布式日志收集的關(guān)鍵步驟和考慮因素,包括日志生成與采集、日志傳輸、日志收集器的高性能網(wǎng)絡(luò)I/O、日志存儲(chǔ)與分析、監(jiān)控與告警系統(tǒng)、擴(kuò)展性與可維護(hù)性等方面,本文給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2025-01-01
  • golang如何實(shí)現(xiàn)mapreduce單進(jìn)程版本詳解

    golang如何實(shí)現(xiàn)mapreduce單進(jìn)程版本詳解

    這篇文章主要給大家介紹了關(guān)于golang如何實(shí)現(xiàn)mapreduce單進(jìn)程版本的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-01-01
  • GoFrame框架數(shù)據(jù)校驗(yàn)之校驗(yàn)結(jié)果Error接口對(duì)象

    GoFrame框架數(shù)據(jù)校驗(yàn)之校驗(yàn)結(jié)果Error接口對(duì)象

    這篇文章主要為大家介紹了GoFrame框架數(shù)據(jù)校驗(yàn)之校驗(yàn)結(jié)果Error接口對(duì)象示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 一文帶你了解Golang中的并發(fā)性

    一文帶你了解Golang中的并發(fā)性

    并發(fā)是一個(gè)很酷的話題,一旦你掌握了它,就會(huì)成為一筆巨大的財(cái)富。所以本文就來(lái)和大家一起來(lái)聊聊Golang中的并發(fā)性,感興趣的可以了解一下
    2023-03-03
  • Go Map并發(fā)沖突預(yù)防與解決

    Go Map并發(fā)沖突預(yù)防與解決

    這篇文章主要為大家介紹了Go Map并發(fā)沖突預(yù)防與解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • golang string、int、int64 float 互相轉(zhuǎn)換方式

    golang string、int、int64 float 互相轉(zhuǎn)換方式

    這篇文章主要介紹了golang string、int、int64 float 互相轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Golang實(shí)現(xiàn)讀取excel文件并轉(zhuǎn)換為JSON格式

    Golang實(shí)現(xiàn)讀取excel文件并轉(zhuǎn)換為JSON格式

    本文介紹了如何使用Golang讀取Excel文件并將其轉(zhuǎn)換為JSON格式,通過(guò)安裝excelize依賴和創(chuàng)建readExcelToJSON方法,可以實(shí)現(xiàn)這一功能,如果需要轉(zhuǎn)換數(shù)據(jù)類型,可以修改相應(yīng)的代碼,需要的朋友可以參考下
    2025-03-03
  • Go讀寫鎖操作方法示例詳解

    Go讀寫鎖操作方法示例詳解

    這篇文章主要為大家介紹了Go讀寫鎖方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07

最新評(píng)論

北票市| 临桂县| 吴江市| 东兰县| 东宁县| 宁海县| 肃南| 旌德县| 延长县| 阿克陶县| 安丘市| 高淳县| 禹城市| 迁安市| 定西市| 修武县| 探索| 麟游县| 彭州市| 双鸭山市| 交口县| 乌恰县| 仲巴县| 大田县| 湟源县| 德化县| 苍溪县| 三河市| 湖北省| 灵寿县| 佳木斯市| 高州市| 中西区| 武平县| 合江县| 皋兰县| 济源市| 汝城县| 花莲县| 溆浦县| 巴南区|