大家好,我叫谢伟,是一名程序员。
近期我会持续更新内置库的学习笔记,主要参考的是文档 godoc 和 内置库的源码
本节的主题:error
Go 中的错误处理和别的语言不一样,设计哲学也不一样,常有开发者埋怨 Go 语言中的错误处理。
本节从内置库的 error 出发,结合内置库的常用的 错误处理手法,总结出适合项目的错误处理方式。
大纲
- 自己的常用的 error 错误处理方式
- 内置库实现的 error
- 总结
自己的总结常用的 error 错误处理方式
1. 创建 error 类型的值
- errors.New()
- fmt.Errorf()
这两个方法即可实现。
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. 自定义错误类型
- 实现 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
自己定义的错误类型,只要实现了 error 接口即可。
可以看下具体的 接口定义:
type error interface {
Error() string
}
- errors.New 的具体实现
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 方法
- fmt.Errorf 的具体实现
func Errorf(format string, a ...interface{}) error {
return errors.New(Sprintf(format, a...))
}
即 fmt.Errorf 调用的也是 errors.New 方法。
总结上文:
- 创建 error 存在两种方法
- 底层 是一个结构体实现了 error 接口
- 自定义的错误类型,实现 Error 方法即可
内置库实现的 error
错误很常见,是业务的一部分,那么内置的库在实现时也会遇到错误,看内置库是如何实现的 error
1. strconv.Atoi
func main(){
number, err := strconv.Atoi("2992-121")
fmt.Println(number,err)
}
>>
0 strconv.Atoi: parsing "2992-121": invalid syntax
具体的底层实现是:
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()
}
定义各种错误类型:
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))}
}
即:核心是定义了一个结构体,实现了 error 接口
2. net/http
func main(){
response, err := http.Get("https://space.bilibili.com/10056291/#/")
fmt.Println(response, err)
}
底层的错误类型是:
type Error struct {
Op string
URL string
Err error
}
func (e *Error) Error() string { return e.Op + " " + e.URL + ": " + e.Err.Error() }
即定义了一个结构体,实现的 error 接口。
鉴于对内置库的底层的 错误类型的阅读,修改自己设置错误类型的方法:如下
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",
}
}
总结
- 创建 error 值存在两种方法
- 自己定义的错误类型,只需实现 Error 方法,实现 error 接口即可
其他:对错误的处理还有这些建议
- 每个库,可以在库文件的开头声明常见的错误
var ErrorIndexOut = errors.New("index out of range")
var ErrorFuck = errors.New("fucking life")
- 错误类型的变量可以带上
Err
或者Error
标示 - 建议统一处理错误类型,即,完整的项目在声明自己的库处理模块,而不是随意的在项目内创建 error
<完>
- 后端工程师攻略第一季迭代进行中