Golang中error和创建error源码解析
try...catch
errorerror
1.什么是error
error
而异常指的是不应该出现问题的地方出现了问题。比如引用了空指针,这种情况在人们的意料之外。
可见,错误是业务过程的一部分,而异常不是 。
errorintfloat64
错误值可以存储在变量中,也可以从函数中返回,等等。
2.error源码
src/builtin/builtin.go
// src/builtin/builtin.go
// 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
}
复制代码
errorError()string
nil
先看一个文件打开错误的例子:
f, err := os.Open("/test.txt")
if err != nil {
fmt.Println("open failed, err:", err)
return
}
fmt.Println("file is :", f)
复制代码
输出:
open failed, err: open /test.txt: The system cannot find the file specified.
复制代码
open/test.txtThe system cannot find the file specified.
err.Error()
这就是错误描述是如何在一行中打印出来的原因。
了解了error是什么,我们接下来了解error的创建。
创建方式有两种:
- errors.New()
- fmt.Errorf()
1.errors.New()函数
src/errors/errors.goerrors.New()
// src/errors/errors.go
// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
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
}
复制代码
New()函数返回一个错误,该错误的格式为给定的文本。
即使文本相同,每次对New的调用也会返回一个不同的错误值。
errorStringstringError()
我们实战一下:
// 1.errors.New() 创建一个 error
err1 := errors.New("这是 errors.New() 创建的错误")
fmt.Printf("err1 错误类型:%T,错误为:%v\n", err1, err1)
复制代码
输出:
err1 错误类型:*errors.errorString,错误为:这是 errors.New() 创建的错误
复制代码
errorStringerrors.
通常这就够了,它能反映当时“出错了”,但是有些时候我们需要更加具体的信息。即需要具体的“上下文”信息,表明具体的错误值。
fmt.Errorf
2.fmt.Errorf()函数
fmt.Errorf()
我们先实战一下,看看和上一节的内容有什么不同:
// 2.fmt.Errorf()
err2 := fmt.Errorf("这个 fmt.Errorf() 创建的错误,错误编码为:%d", 404)
fmt.Printf("err2 错误类型:%T,错误为:%v\n", err2, err2)
复制代码
输出:
err2 错误类型:*errors.errorString,错误为:这个 fmt.Errorf() 创建的错误,错误编码为:404
复制代码
err2*errors.errorString
err2*errors.errorStringfmt.Errorf()
我们先看下其源码实现:
// src/fmt/errors.go
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
}
复制代码
p.wrappedErrnilerrors.New()
err2*errors.errorString
p.wrappedErrnil
我们先看个例子:
// 3. go 1.13 新增加的错误处理特性 %w
err3 := fmt.Errorf("err3: %w", err2) // err3包裹err2错误
fmt.Printf("err3 错误类型:%T,错误为:%v\n", err3, err3)
复制代码
输出:
err3 错误类型:*fmt.wrapError,错误为:err3: 这个 fmt.Errorf() 创建的错误,错误编码为:404
复制代码
%werror
err3err2
err3: 这个 fmt.Errorf() 创建的错误,错误编码为:404
复制代码
err3*fmt.wrapError
好了,带着这些问题,我们从头开始捋一捋源码,就知道它们到底是什么?
fmt.Errorf()p := newPrinter()ppnewPrinter()
// src/fmt/print.go
// newPrinter allocates a new pp struct or grabs a cached one.
func newPrinter() *pp {
p := ppFree.Get().(*pp)
p.panicking = false
p.erroring = false
p.wrapErrs = false
p.fmt.init(&p.buf)
return p
}
复制代码
newPrinter()pp
p.wrappedErr
// src/fmt/print.go
// pp is used to store a printer's state and is reused with sync.Pool to avoid allocations.
type pp struct {
...
...
// wrapErrs is set when the format string may contain a %w verb.
wrapErrs bool
// wrappedErr records the target of the %w verb.
wrappedErr error
}
复制代码
pp
wrapErrs%wwrappedErr%werr2
p.wrappedErrnil
p.wrappedErr%wnil
*fmt.wrapError
elsep.wrappedErrnil
err = &wrapError{s, p.wrappedErr}
复制代码
errwrapError&wrapError
// src/fmt/errors.go
type wrapError struct {
msg string
err error
}
func (e *wrapError) Error() string {
return e.msg
}
func (e *wrapError) Unwrap() error {
return e.err
}
复制代码
wrapError
stringerror
实现了两个方法:
wrapErrorerrorerrormsg
*fmt.wrapError
fmt.Errorf()p.doPrintf(format, a)
errors.New()*errors.errorString
fmt.Errorf()fmt.Errorf()
%wp.wrappedErrnilerrors.New()*errors.errorString%wp.wrappedErrnilwrapError*fmt.wrapError