大家好,我叫謝偉抛腕,是一名程序員芋绸。
近期我會(huì)持續(xù)更新內(nèi)置庫(kù)的學(xué)習(xí)筆記,主要參考的是文檔 godoc 和 內(nèi)置庫(kù)的源碼
本節(jié)的主題:error
Go 中的錯(cuò)誤處理和別的語(yǔ)言不一樣担敌,設(shè)計(jì)哲學(xué)也不一樣摔敛,常有開(kāi)發(fā)者埋怨 Go 語(yǔ)言中的錯(cuò)誤處理。
本節(jié)從內(nèi)置庫(kù)的 error 出發(fā)柄错,結(jié)合內(nèi)置庫(kù)的常用的 錯(cuò)誤處理手法舷夺,總結(jié)出適合項(xiàng)目的錯(cuò)誤處理方式苦酱。
大綱
- 自己的常用的 error 錯(cuò)誤處理方式
- 內(nèi)置庫(kù)實(shí)現(xiàn)的 error
- 總結(jié)
自己的總結(jié)常用的 error 錯(cuò)誤處理方式
1. 創(chuàng)建 error 類(lèi)型的值
- errors.New()
- fmt.Errorf()
這兩個(gè)方法即可實(shí)現(xiàn)售貌。
package main
import (
"errors"
"fmt"
"reflect"
)
func main() {
recordError := errors.New("record not found")
dbError := fmt.Errorf("%s", "db connet fail")
fmt.Println(recordError, reflect.TypeOf(recordError))
fmt.Println(dbError, reflect.TypeOf(dbError))
}
>>
record not found *errors.errorString
db connet fail *errors.errorString
2. 自定義錯(cuò)誤類(lèi)型
- 實(shí)現(xiàn) error 接口
type CodeError struct {
Code int
Message string
}
func (c CodeError) Error() string {
return fmt.Sprintf(" e.Code = %d, e.Message=%s", c.Code, c.Message)
}
func Result() error {
var codeError CodeError
codeError = CodeError{
Code: 400,
Message: "connect fail",
}
return codeError
}
func main(){
var codeError CodeError
var err error
codeError = CodeError{
Code: 404,
Message: "http status code error",
}
err = codeError
fmt.Println(err)
}
>>
e.Code = 404, e.Message=http status code error
自己定義的錯(cuò)誤類(lèi)型给猾,只要實(shí)現(xiàn)了 error 接口即可。
可以看下具體的 接口定義:
type error interface {
Error() string
}
- errors.New 的具體實(shí)現(xiàn)
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 結(jié)構(gòu)體, 綁定了 Error 方法
- fmt.Errorf 的具體實(shí)現(xiàn)
func Errorf(format string, a ...interface{}) error {
return errors.New(Sprintf(format, a...))
}
即 fmt.Errorf 調(diào)用的也是 errors.New 方法颂跨。
總結(jié)上文:
- 創(chuàng)建 error 存在兩種方法
- 底層 是一個(gè)結(jié)構(gòu)體實(shí)現(xiàn)了 error 接口
- 自定義的錯(cuò)誤類(lèi)型敢伸,實(shí)現(xiàn) Error 方法即可
內(nèi)置庫(kù)實(shí)現(xiàn)的 error
錯(cuò)誤很常見(jiàn),是業(yè)務(wù)的一部分恒削,那么內(nèi)置的庫(kù)在實(shí)現(xiàn)時(shí)也會(huì)遇到錯(cuò)誤池颈,看內(nèi)置庫(kù)是如何實(shí)現(xiàn)的 error
1. strconv.Atoi
func main(){
number, err := strconv.Atoi("2992-121")
fmt.Println(number,err)
}
>>
0 strconv.Atoi: parsing "2992-121": invalid syntax
具體的底層實(shí)現(xiàn)是:
type NumError struct {
Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat)
Num string // the input
Err error // the reason the conversion failed (e.g. ErrRange, ErrSyntax, etc.)
}
func (e *NumError) Error() string {
return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error()
}
定義各種錯(cuò)誤類(lèi)型:
var ErrRange = errors.New("value out of range")
var ErrSyntax = errors.New("invalid syntax")
func syntaxError(fn, str string) *NumError {
return &NumError{fn, str, ErrSyntax}
}
func rangeError(fn, str string) *NumError {
return &NumError{fn, str, ErrRange}
}
func baseError(fn, str string, base int) *NumError {
return &NumError{fn, str, errors.New("invalid base " + Itoa(base))}
}
func bitSizeError(fn, str string, bitSize int) *NumError {
return &NumError{fn, str, errors.New("invalid bit size " + Itoa(bitSize))}
}
即:核心是定義了一個(gè)結(jié)構(gòu)體,實(shí)現(xiàn)了 error 接口
2. net/http
func main(){
response, err := http.Get("https://space.bilibili.com/10056291/#/")
fmt.Println(response, err)
}
底層的錯(cuò)誤類(lèi)型是:
type Error struct {
Op string
URL string
Err error
}
func (e *Error) Error() string { return e.Op + " " + e.URL + ": " + e.Err.Error() }
即定義了一個(gè)結(jié)構(gòu)體钓丰,實(shí)現(xiàn)的 error 接口躯砰。
鑒于對(duì)內(nèi)置庫(kù)的底層的 錯(cuò)誤類(lèi)型的閱讀,修改自己設(shè)置錯(cuò)誤類(lèi)型的方法:如下
type ExampleError struct {
Err error
Code int
Message string
}
func (e *ExampleError) Error() string {
return fmt.Sprintf("e.Code = %d e.Err = %s e.Message = %s", e.Code, e.Err.Error(), e.Message)
}
func ExampleResult() error {
return &ExampleError{
Err: errors.New("what the fucking life"),
Code: 502,
Message: "what the fucking life",
}
}
總結(jié)
- 創(chuàng)建 error 值存在兩種方法
- 自己定義的錯(cuò)誤類(lèi)型携丁,只需實(shí)現(xiàn) Error 方法琢歇,實(shí)現(xiàn) error 接口即可
其他:對(duì)錯(cuò)誤的處理還有這些建議
- 每個(gè)庫(kù),可以在庫(kù)文件的開(kāi)頭聲明常見(jiàn)的錯(cuò)誤
var ErrorIndexOut = errors.New("index out of range")
var ErrorFuck = errors.New("fucking life")
- 錯(cuò)誤類(lèi)型的變量可以帶上
Err
或者Error
標(biāo)示 - 建議統(tǒng)一處理錯(cuò)誤類(lèi)型梦鉴,即李茫,完整的項(xiàng)目在聲明自己的庫(kù)處理模塊,而不是隨意的在項(xiàng)目?jī)?nèi)創(chuàng)建 error
<完>