errors

errors

https://pkg.go.dev/errors@go1.20.1

​ errors 包实现了操作错误的函数。

​ New函数创建的错误只包含文本消息。

​ 如果e的类型具有下列方法之一:

1
2
Unwrap() error
Unwrap() []error

​ 那么e就包装了另一个错误。如果e.Unwrap()返回非nil的错误w或包含w的切片,则我们说e包装了w。Unwrap方法返回的nil错误表示e不包装任何错误。Unwrap方法返回一个包含nil错误值的[]error是无效的。

使用Unwrap方法的示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

import (
	"fmt"
)

type myError1 struct {
	Name string
}

func (e myError1) Error() string {
	return fmt.Sprintf("name=%v", e.Name)
}

type myError2 struct {
	Desc string
	Err  error
}

func (e myError2) Error() string {
	return fmt.Sprintf("Desc:%v,Err:%v", e.Desc, e.Err)
}

func (e myError2) Unwrap() error {
	return e.Err
}

func main() {
	err := myError2{"zlx1", myError1{"zlx"}}
	fmt.Println(err)
	fmt.Println(err.Unwrap())

	err2 := fmt.Errorf("err2=%w", err)
	fmt.Println(err2)
}
Output:

Desc:zlx1,Err:name=zlx
name=zlx
err2=Desc:zlx1,Err:name=zlx

​ 创建包装错误的简单方法是调用fmt.Errorf并对错误参数应用%w占位符:

wrapsErr := fmt.Errorf("... %w ...", ..., err, ...)

​ 对错误进行连续的解包将创建一棵树。Is和As函数通过先检查错误本身,然后依次检查其每个子树(先序,深度优先遍历)来检查错误的树。

​ Is函数检查其第一个参数的树,查找与第二个参数匹配的错误。它报告它是否找到匹配项。应优先使用它,而不是简单的相等性检查:

1
if errors.Is(err, fs.ErrExist)

优于

1
if err == fs.ErrExist

因为前者将成功地匹配err包装了fs.ErrExist。

​ As检查其第一个参数的树,查找可以分配给其第二个参数的错误,该参数必须为指针。如果成功,则执行分配并返回true。否则,它将返回false。下面这种形式是优先使用的:

1
2
3
4
var perr *fs.PathError
if errors.As(err, &perr) {
	fmt.Println(perr.Path)
}

优于

1
2
3
if perr, ok := err.(*fs.PathError); ok {
	fmt.Println(perr.Path)
}

​ 因为前者将成功地匹配err包装了*fs.PathError

Example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import (
	"fmt"
	"time"
)

// MyError 是一个实现了包含时间和消息的错误类型。
type MyError struct {
	When time.Time
	What string
}

func (e MyError) Error() string {
	return fmt.Sprintf("%v: %v", e.When, e.What)
}

func oops() error {
	return MyError{
		time.Date(1989, 3, 15, 22, 30, 0, 0, time.UTC),
		"the file system has gone away",
	}
}

func main() {
	if err := oops(); err != nil {
		fmt.Println(err)
	}
}
Output:

1989-03-15 22:30:00 +0000 UTC: the file system has gone away

常量

This section is empty.

变量

This section is empty.

函数

func As <- go1.13

1
func As(err error, target any) bool

​ As 函数在 err 的错误树中查找第一个与 target 匹配的错误,如果找到,则将 target 设置为该错误的值并返回 true。否则,返回 false。

​ 错误树包括 err 本身,以及通过反复调用 Unwrap 获得的错误。当 err 包含多个错误时,As 按深度优先遍历顺序依次检查 err 的每个子错误。

​ 如果错误的具体值可以分配给 target 指向的值,则错误与 target 匹配,或者如果错误有一个 As(target) bool 方法,则该方法返回 true。在后一种情况下,As 方法负责设置 target。

​ 错误类型可能会提供 As 方法,以便可以将其视为不同的错误类型。

​ 如果 target 不是一个实现了 error 接口的类型或任何接口类型的非 nil 指针,则 As 函数会引发 panic。

As Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
	"errors"
	"fmt"
	"io/fs"
	"os"
)

func main() {
	if _, err := os.Open("non-existing"); err != nil {
		var pathError *fs.PathError
		if errors.As(err, &pathError) {
			fmt.Println("Failed at path:", pathError.Path)
		} else {
			fmt.Println(err)
		}
	}

}
Output:

Failed at path: non-existing

func Is <- go1.13

1
func Is(err, target error) bool

​ Is 函数报告 err 的错误树中是否有任何错误与 target 匹配。

​ 错误树包括 err 本身,以及通过反复调用 Unwrap 获得的错误。当 err 包含多个错误时,Is 按深度优先遍历顺序依次检查 err 的每个子错误。

​ 如果一个错误等于 target 或者实现了一个 Is(error) bool 方法,使得 Is(target) 返回 true,则认为该错误与 target 匹配。

​ 错误类型可能会提供 Is 方法,以便可以将其视为与现有错误等效。例如,如果 MyError 定义了

1
func (m MyError) Is(target error) bool { return target == fs.ErrExist }

​ 那么 Is(MyError{}, fs.ErrExist) 返回 true。标准库中的 syscall.Errno.Is 就是一个示例。Is 方法只应该浅层比较 err 和 target,不要对它们中的任何一个调用 Unwrap。

Is Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
	"errors"
	"fmt"
	"io/fs"
	"os"
)

func main() {
	if _, err := os.Open("non-existing"); err != nil {
		if errors.Is(err, fs.ErrNotExist) {
			fmt.Println("file does not exist")
		} else {
			fmt.Println(err)
		}
	}

}
Output:

file does not exist

func Join <- go1.20

1
func Join(errs ...error) error

​ Join 函数返回一个包含给定错误的错误。任何 nil 错误值都会被丢弃。如果 errs 不包含任何非 nil 值,则返回 nil。该错误的格式为 errs 中每个元素的 Error 方法返回值的串联,每个字符串之间都有一个换行符。

Join Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import (
	"errors"
	"fmt"
)

func main() {
	err1 := errors.New("err1")
	err2 := errors.New("err2")
	err := errors.Join(err1, err2)
	fmt.Println(err)
	if errors.Is(err, err1) {
		fmt.Println("err is err1")
	}
	if errors.Is(err, err2) {
		fmt.Println("err is err2")
	}
}
Output:

err1
err2
err is err1
err is err2

func New

1
func New(text string) error

​ New 函数返回一个错误,该错误格式化为给定的文本。即使文本相同,每次调用 New 都会返回一个不同的错误值。

New Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import (
	"errors"
	"fmt"
)

func main() {
	err := errors.New("emit macho dwarf: elf header corrupted")
	if err != nil {
		fmt.Print(err)
	}
}
Output:

emit macho dwarf: elf header corrupted
New Example (Errorf)

​ fmt 包的 Errorf 函数允许我们使用该包的格式化功能来创建描述性错误消息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import (
	"fmt"
)

func main() {
	const name, id = "bimmler", 17
	err := fmt.Errorf("user %q (id %d) not found", name, id)
	if err != nil {
		fmt.Print(err)
	}
}
Output:

user "bimmler" (id 17) not found

func Unwrap <- go1.13

1
func Unwrap(err error) error

​ Unwrap 函数返回 err 上的 Unwrap 方法的结果,如果 err 的类型包含返回错误的 Unwrap 方法。否则,Unwrap 函数返回 nil。

​ 如果 Unwrap 方法返回 []error,则 Unwrap 函数返回 nil。

Unwrap Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
	"errors"
	"fmt"
)

func main() {
	err1 := errors.New("error1")
	err2 := fmt.Errorf("error2: [%w]", err1)
	fmt.Println(err2)
	fmt.Println(errors.Unwrap(err2))
	// Output
	// error2: [error1]
	// error1
}
Output:

error2: [error1]
error1

类型

This section is empty.