errors 编程

Go 1.13引入了一个增强的package errors(即xerrors),它大致标准化了带有错误的编程。就我个人而言,我觉得这个API令人困惑。这是一个如何有效使用它的快速参考。

创建errors

前哨错误的工作原理与以前相同。将它们命名为ErrXxx,并使用errors.New创建它们。

1
var ErrFoo = errors.New("foo error")

错误类型的工作原理与以前基本相同。将它们命名为XxxError,并确保它们有一个错误方法,以满足错误接口。

1
2
3
4
5
6
7
type BarError struct {
Reason string
}

func (e BarError) Error() string {
return fmt.Sprintf("bar error: %s", e.Reason)
}

如果您的错误类型包装了另一个错误,请提供一个Unwrap方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type BazError struct {
Reason string
Inner error
}

func (e BazError) Error() string {
if e.Inner != nil {
return fmt.Sprintf("baz error: %s: %v", e.Reason, e.Inner)
}
return fmt.Sprintf("baz error: %s", e.Reason)
}

func (e BazError) Unwrap() error {
return e.Inner
}

包装和返回错误

默认情况下,当您在函数中遇到错误并需要将其返回给调用者时,使用fmt.Errorf和新的%w谓词将其与有关错误的上下文包装起来。

1
2
3
4
5
func process(j Job) error {
result, err := preprocess(j)
if err != nil {
return fmt.Errorf("error preprocessing job: %w", err)
}

这个过程称为错误注释。避免返回未注释的错误,因为这会使调用者很难理解哪里出错了。

此外,对于更复杂的用例,可以考虑通过自定义错误类型(如上面的BazError)包装错误。这个过程称为错误注释。避免返回未注释的错误,因为这会使调用者很难理解哪里出错了。

此外,对于更复杂的用例,可以考虑通过自定义错误类型(如上面的BazError)包装错误。

1
2
3
4
5
   p := getPriority()
widget, err := manufacture(p, result)
if err != nil {
return ManufacturingError{Priority: p, Error: err}
}

检查错误

大多数时候,当您收到错误时,您并不关心细节。无论您尝试做什么都失败了,所以您要么需要报告错误(例如,记录它),然后继续;或者,如果无法继续,则使用上下文注释错误,并将其返回给调用者。

如果关心接收到的错误,可以使用errors.Is检查前哨错误,并使用errors.As检查错误值。

1
2
3
4
5
6
7
8
9
10
11
12
err := f()
if errors.Is(err, ErrFoo) {
// 你知道你错了
// 做出适当的回应
}

var bar BarError
if errors.As(err, &bar) {
// 你知道你有个BarError
// bar的字段被填充
// 做出适当的回应
}

errors.Is和errors.As都将尝试递归地展开错误,以便找到匹配项。这段代码演示了基本的错误包装和检查技术。查看func a()中的检查顺序,然后尝试更改func c()返回的错误,以直观地了解所有内容是如何工作的。

正如包错误文档所述,宁可使用errors.Is,也不要检查普通的等式,例如if err == ErrFoo;并且更喜欢使用errors.As而不是普通类型断言,例如if e, ok:= err.(MyError),因为普通版本不执行展开。如果您显式地不允许调用方打开错误,请为fmt提供一个不同的格式化谓词。Errorf,比如% v;或者不对错误类型提供Unwrap方法。但这种情况应该很少见。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2021 朝着牛逼的道路一路狂奔 All Rights Reserved.

访客数 : | 访问量 :