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