0717-7821348
欢乐彩直播结果

欢乐彩直播结果

您现在的位置: 首页 > 欢乐彩直播结果
欢乐彩-最威望的解说Go 1.13中的错误处理
2019-12-19 01:27:00

介绍

在曩昔的十年中, Go的errors are values的理念在编码实践中运行得也很杰出。虽然规范库对过错处理的的支撑很少(只要errors.New和fmt.Errorf函数能够用来结构仅包括字符串音讯的过错),可是内置的error接口使Go程序员能够增加所需的任何信息。它所需求的仅仅一个完成Error办法的类型:

type QueryError struct 欢乐彩-最威望的解说Go 1.13中的错误处理{
Query string
Err error
}
func (e *QueryError) Error() string { return e.Query + ": " + e.Err.Error() }

像这样的过错类型无处不在,它们存储的信息改动很大,从时刻戳到文件名再到服务器地址。一般,该信息包括另一个较低等级的过错以供给其他上下文信息。

在Go代码中,运用一个包括了另一个过错的过错类型的方法非常遍及,以至于经过广泛评论后,Go 1.13为其增加了清晰的支撑。这篇文章描绘了规范库供给的支撑:errors包中的三个新功能,以及fmt.Errorf中增加的新格式化动词。

在详细描绘这些改动之前,让咱们先回忆一下在Go言语的前期版别中怎么查看和结构过错。

Go 1.13版别之前的过错处理

查看过错

过错是值(errors are values)。程序经过几种办法依据这些值来做出决议计划。最常见的是经过与nil的比较来确认操作是否失利。

if err != nil {
// 出错了!
}

有时咱们将过错与已知的前哨值(sentinel value)进行比较来查看是否发作了特定过错。比方:

var ErrNotFound = errors.New("not found")
if err == ErrNotFound {
// something wasn't found
}

过错值能够是满意言语界说的error 接口的任何类型。程序能够运用类型断语(type assertion)或类型开关(type switch)来判别过错值是否可被视为特定的过错类型。

type NotFoundError struct {
Name string
}
func (e *NotFoundError) Error() string { return e.Name + ": not found" }
if e, ok := err.(*NotFoundError); ok {
// e.Name wasn't found
}

增加信息

函数一般在将过错向上传递给调用仓库时增加额定过错信息,例如对过错发作时所发作状况的简略描绘。一种简略的办法是结构一个新过错,并在其间包括上一个过错:

if err != nil {
return fmt.Errorf("decompress %v: %v", name, err)
}

运用fmt.Errorf创立的新过错将丢掉原始过错中的一切内容(文本在外)。就像咱们在前面所看到的QueryError那样,有时咱们或许想要界说一个包括根底过错的新过错类型,并将其保欢乐彩-最威望的解说Go 1.13中的错误处理存下来以供代码查看。咱们再次来看一下QueryError:

type QueryError struct {
Query string
Err error
}

程序能够查看一个*QueryError值的内部以依据潜在的过错进行决议计划。有时您会看到称为“打开”过错的信息。

if e, ok := err.(*QueryError); ok && e.Err == ErrPermission {
// query failed because of a permission problem
}

规范库中的os.PathError类型便是别的一个在过错中包括另一个过错的示例。

Go 1.13版别的过错处理

Unwrap办法

Go 1.13在errors和fmt规范库包中引入了新功能以简化处理包括其他过错的过错。其间最重要的不是改动,而是一个约好:包括另一个过错的过错能够完成Unwrap办法来回来所包括的底层过错。假如e1.Unwrap()回来了e2,那么咱们说e1包装了e2,您能够Unwrap e1来得到e2

遵从此约好,咱们能够为上面的QueryError类型供给一个Unwrap办法来回来其包括的过欢乐彩-最威望的解说Go 1.13中的错误处理错:

func (e *QueryError) Unwrap() error { return e.Err }

Unwrap过错的成果自身(底层过错)或许也具有Unwrap办法。咱们将这种经过重复unwrap而得到的过错序列为过错链。

运用Is和As查看过错

Go 1.13的errors包中包括了两个用于查看过错的新函数:Is和As。

errors.Is函数将过错与值进行比较。

// Similar to:
// if err == ErrNotFound { … }
if errors.Is(err, ErrNotFound) {
// something wasn't found
}

As函数用于测验过错是否为特定类型。

// Similar to:
// if e, ok := err.(*QueryError); ok { … }
var e *QueryError
if errors.As(err, &e) {
// err is a *QueryError, and e is set to the error's value
}

在最简略的状况下,errors.Is函数的行为类似于上面临岗兵过错(sentinel error))的比较,而errors.As函数的行为类似于类型断语(type assertion)。可是,在处理包装过错(包括其他过错的过错)时,这些函数会考虑过错链中的一切过错。让咱们再次看一下经过打开QueryError以查看潜在过错:

if e, ok := err.(*QueryError); ok && e.Err == ErrPermission {
// query failed because of a permission problem
}

运用errors.Is函数,咱们能够这样写:

if errors.Is(err, ErrPermission) {
// err, or some error that it wraps, is a permission problem
}

errors包还包括一个新Unwrap函数,该函数回来调用过错Unwrap办法的成果,或许当过错没有Unwrap办法时回来nil。一般咱们最好运用errors.Is或errors.As,因为这些函数将在单个调用中查看整个过错链。

用%w包装过错

如前面所述,咱们一般运用fmt.Errorf函数向过错增加其他信息。

if err != nil {
return fmt.Errorf("decompress %v: %v", name, err)
}

在Go 1.13中,fmt.Errorf函数支撑新的%w动词。当存在该动词时,所回来的过错fmt.Errorf将具有Unwrap办法,该办法回来参数%w对应的过错。%w对应的参数有必要是过错(类型)。在一切其他方面,%w与%v同等。

if err != nil {
// Return an error which unwraps to err.
return fmt.Errorf("decompress %v: %w", name, err)
}

运用%w创立的包装过错可用于errors.Is和errors.As:

err := fmt.Errorf("access denied: %w”, ErrPermission)
...
if errors.Is(err, ErrPermission) ...

是否包装

在运用fmt.Errorf或经过完成自界说类型将其他上下文增加到过错时,您需求确认新过错是否应该包装原始过错。这个问题没有一致答案。它取决于创立新过错的上下文。包装过错将会被揭露给调用者。假如要防止露出完成细节,那么请不要包装过错。

举一个比如,假定一个Parse函数从io.Reader读取杂乱的数据结构。假如发作过错,咱们期望陈述发作过错的行号和列号。假如从io.Reader读取miracle时发作过错,咱们将包装该过错以供查看底层问题。因为调用者为函数供给了io.Reader,因而有理由揭露它发生的过错。

相反,一个对数据库进行屡次调用的函数或许不该该将其间调用之一的成果解开的过错回来。假如该函数运用的数据库是完成细节,那么露出这些过错便是对笼统的违背。例如,假如你的程序包pkg中的函数LookupUser运用了Go的database/sql程序包,则或许会遇到sql.ErrNoRows过错。假如运用fmt.Errorf("accessing DB: %v", err)来回来该过错,则调用者无法检视到内部的sql.ErrNoRows。可是,假如函数运用fmt.Errorf("accessing DB: %w", err)回来过错,则调用者能够编写下面代码:

err := pkg.LookupUser(...)
if errors.Is(err, sql.ErrNoRows) …

此刻,假如您不期望对客户端源码发生影响,该函数也有必要一直回来sql.ErrNoRows,即便您切换到其他数据库程序包。换句话说,包装过错会使该过错成为您API的一部分。假如您不想将来将过错作为API的一部分来支撑,则不该包装该过错。

重要的是要记住,不管是否包装过错,过错文本都将相同。那些企图了解过错的人将得到相同的信息,不管选用哪种办法; 是否要包装过错的挑选是关于是否要给程序供给更多信息,以便他们能够做出更正确的决议计划,仍是保存该信息以保存笼统层。

运用Is和As办法自界说过错测验

errors.Is函数查看过错链中的每个过错是否与方针值匹配。默许状况下,假如两者持平,则过错与方针匹配。别的,链中的过错或许会经过完成Is办法来声明它与方针匹配。

例如,下面的过错类型界说是受Upspin error包的启示,它将过错与模板进行了比较,而且仅考虑模板中非零的字段:

type Error struct {
Path string
User string
}
func (e *Error) Is(target error) bool {
t, ok := target.(*Error)
if !ok {
return false
}
return (e.Path == t.Path || t.Path == "") &&
(e.User == t.User || t.User == "")
}
if errors.Is(err, &Error{User: "someuser"}) {
// err's User field is "someuser".
}

相同,errors.As函数将运用链中某个过错的As办法,假如该过错完成了As办法。

过错和包API

回来过错的程序包(大多数都会回来过错)应描绘程序员或许依靠的那些过错的特点。一个经过精心设计的程序包也将防止回来带有不该依靠的特点的过错。

最简略的规约是用于阐明操作欢乐彩-最威望的解说Go 1.13中的错误处理成功或失利的特点,别离回来nil或non-nil过错值。在许多状况下,不需求进一步的信息了。

假如咱们期望函数回来可辨认的过错条件,例如“item not found”,则或许会回来包装岗兵的过错。

var ErrNotFound = errors.New("not found")
// FetchItem returns the named item.
//
// If no item with the name exists, FetchItem returns an error
// wrapping ErrNotFound.
func FetchItem(name string) (*Item, error) {
if itemNotFound(name) {
return nil, fmt.Errorf("%q: %w", name, ErrNotFound)
}
// ...
}

还有其他现有的供给过错的方法,能够由调用方进行语义查看,例如直接回来岗兵值,特定类型或能够运用谓词函数查看的值。

在一切状况下,都应留意不要向用户揭露内部细节。正如咱们在上面的“是否要包装”中说到的那样,当您从另一个包中回来过错时,应该将过错转换为不露出根本过错的方法,除非您乐意将来再回来该特定过错。

f, err := os.Open(filename)
if err != nil {
// The *os.PathError re欢乐彩-最威望的解说Go 1.13中的错误处理turned by os.Open is an internal detail.
// To avoid exposing it to the caller, repackage it as a new
// error with the same text. We use the %v formatting verb, since
// %w would permit the caller to unwrap the original *os.PathError.
return fmt.Errorf("%v", err)
}

假如将函数界说为回来包装某些符号或类型的过错,请不要直接回来根底过错。

var ErrPermission = errors.New("permission denied")
// DoSomething returns an error wrapping ErrPermission if the user
// does not have permission to do something.
func DoSomething() {
if !userHasPermission() {
// If we return ErrPermission directly, callers might come
// to depend on the exact error value, writing code like this:
//
// if err := pkg.DoSomething(); err == pkg.ErrPermission { … }
//
// This will cause problems if we want to add additional
// context to the error in the future. To avoid this, we
// return an error wrapping the sentinel so that users must
// always unwrap it:
//
// if err := pkg.DoSomething(); errors.Is(err, pkg.ErrPermission) { ... }
return fmt.Errorf("%w", ErrPermission)
}
// ...
}

定论

虽然咱们评论的更改仅包括三个函数和一个格式化动词(%w),但咱们期望它们能大幅改进Go程序中过错处理的办法。咱们期望经过包装来供给其他上下文的办法得到Gopher们地遍及运用,然后协助程序做出更好的决议计划,并协助程序员更快地发现过错。

正如Russ Cox欢乐彩-最威望的解说Go 1.13中的错误处理在GopherCon 2019主题讲演中所说的那样,在Go2的道路上,咱们进行了试验,简化和发布。现在,咱们现已发布了这些更改,咱们等待接下来的试验。

本文翻译自Go官方博客:《Working with Errors in Go 1.13》

译者:tonybai

原文链接:https://tonybai.com/2019/10/18/errors-handling-in-go-1-13/