Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go Error #173

Open
Wang-Kai opened this issue Oct 7, 2021 · 0 comments
Open

Go Error #173

Wang-Kai opened this issue Oct 7, 2021 · 0 comments

Comments

@Wang-Kai
Copy link
Owner

Wang-Kai commented Oct 7, 2021

基本定义

error 是 go 语言的内置类型,其定义很简单,只要实现 Error() string 即可。

// 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
}

日常使用经验

error 处理在日常编程中十分常用,官方对于错误处理也有指导性观点:

But remember: Whatever you do, always check your errors!

如何优雅的使用 error,导向性一定是能够快速的发现问题、定位问题,在此前提之上保持代码简洁、可读性强。

一、预先定义错误

正确的使用 error ,一定是以快速定位错误为目标的。如果能知道是什么错误,那一定能够更好的处理这个错误。预先定义好可能出现的错误,让调用方有一个初步的预判是一个不错的方案。

Go 源码 io Package 就预先定义了常见的错误,调用方可以 通过类似 err == EOF 操作来决定如何更准确的处理这个错误。

// ErrShortWrite means that a write accepted fewer bytes than requested
// but failed to return an explicit error.
var ErrShortWrite = errors.New("short write")

// errInvalidWrite means that a write returned an impossible count.
var errInvalidWrite = errors.New("invalid write result")

// ErrShortBuffer means that a read required a longer buffer than was provided.
var ErrShortBuffer = errors.New("short buffer")

// EOF is the error returned by Read when no more input is available.
// (Read must return EOF itself, not an error wrapping EOF,
// because callers will test for EOF using ==.)
// Functions should return EOF only to signal a graceful end of input.
// If the EOF occurs unexpectedly in a structured data stream,
// the appropriate error is either ErrUnexpectedEOF or some other error
// giving more detail.
var EOF = errors.New("EOF")

// ErrUnexpectedEOF means that EOF was encountered in the
// middle of reading a fixed-size block or data structure.
var ErrUnexpectedEOF = errors.New("unexpected EOF")

// ErrNoProgress is returned by some clients of a Reader when
// many calls to Read have failed to return any data or error,
// usually the sign of a broken Reader implementation.
var ErrNoProgress = errors.New("multiple Read calls return no data or error")

二、使用语言来简化错误处理

《Errors are values》 这篇文章中用三段论的口吻着重提到:Values can be programmed, and since errors are values, errors can be programmed。使用 “编程的思维” 来处理 error,以下是日常业务中常见的代码,看起来重复又丑陋。

_, err = fd.Write(p0[a:b])
if err != nil {
    return err
}
_, err = fd.Write(p1[c:d])
if err != nil {
    return err
}
_, err = fd.Write(p2[e:f])
if err != nil {
    return err
}
// and so on

其实稍微加以结构化改造,代码就可以优雅一些,这个需要思考与设计。改造后的代码错误仅处理一次,如果一开始 write 方法就遇到了错误,后面 write 方法将成为一个 no-op 函数,但遇到的 error 被保留了下来。

type errWriter struct {
    w   io.Writer
    err error
}
func (ew *errWriter) write(buf []byte) {
    if ew.err != nil {
        return
    }
    _, ew.err = ew.w.Write(buf)
}

ew := &errWriter{w: fd}
ew.write(p0[a:b])
ew.write(p1[c:d])
ew.write(p2[e:f])
// and so on
if ew.err != nil {
    return ew.err
}

Go 1.13 以后的错误处理思路

Russ Cox 提出 experiment, simplify and ship,error 的处理改进也是吸收了许多社区里的实践经验,其中最主要的是 pkg/errors。errors 包使用十分广泛,但源码并不多,核心提供了如下能力:

  1. WithMessage(err error, message string) error 构造类似 error chain,新创建一个 error,新 error 会关联传入的 error
  2. Wrap(err error, message string) error 同样是构建 error chain,但加入了错误 stack 信息(哪个文件哪一行报了错)
  3. Cause(err error) error 递归检索最顶层错误(也就是错误根因),直到遇到没有实现 Cause() error 方法的 error

总结下来,pkg/errors 在 Go 语言错误处理上提供了几个能力,简洁而优雅的处理了:

  1. 让 error 拥有上下文,真实场景下错误也是层层上抛、一层套一层的
  2. 便捷的找到问题之源,也就是 Cause 所做的

鉴于社区经验,go 1.13 之后对 error 处理新增了几项支持

  1. 新增 Unwrap 方法, error 就像一条链一样,一个 wrap 另一个,Unwrap 方法返回其 wrap 的 error
  2. 支持 Is & AsIs 用来替代 err == SomeErrorAs 用来替代 switch e := err.(type)
  3. fmt.Errorf 支持新的 Verb %w,其会返回一个新的 error,该 error 的 Unwrap 方法返回占位符中传入的 error

参考文章

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant