Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Golang errors包

Golang errors包快速上手

作者:恋喵大鲤鱼

errors 包是用于处理错误的标准库, errors 包提供的功能比较简单,使用起来非常方便,下面就来介绍一下,感兴趣的可以了解一下

在 Golang 中,errors 包是用于处理错误的标准库, errors 包提供的功能比较简单,使用起来非常方便。

接下来具体讲解一下 errors 包提供的变量、类型和函数。

1.变量

errors 包只定义了一个全局变量 ErrUnsupported。

var ErrUnsupported = New("unsupported operation")

ErrUnsupported 表示请求的操作不能执行,因为它不受支持。例如,调用os.Link()当使用的文件系统不支持硬链接时。

函数和方法不应该返回这个错误,而应该返回一个包含适当上下文的错误,满足:

errors.Is(err, errors.ErrUnsupported)

要么直接包装 ErrUnsupported,要么实现一个 Is 方法。

函数和方法应该说明何种情况下会返回包含 ErrUnsupported 的错误。

2.类型

error 是一个内建的接口类型,任何类型只要实现了 Error() string 方法,就实现了 error 接口,这意味着该类型的实例可以被当作一个 error 来处理。

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

3.函数

3.1 New

errors.New 用于创建一个新的错误对象。它接收一个字符串作为错误消息,并返回一个错误对象。

func New(text string) error

我们可以看下其具体实现:

// 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 返回的是实现了 error 接口的具体类型 *errorString。

3.2 Is

简介

errors.Is 函数是一个用于错误处理的核心工具,用于检查错误链(error chain)中是否存在某个特定的错误实例。

它是 Go 1.13 版本引入的错误处理增强功能之一,与 errors.As 和 errors.Unwrap 共同提供了更灵活的错误处理机制。

函数签名

func Is(err, target error) bool

核心功能

示例代码

package main

import (
    "errors"
    "fmt"
)

var ErrNotFound = errors.New("not found")

func main() {
    err := fmt.Errorf("context: %w", ErrNotFound)
    if errors.Is(err, ErrNotFound) {
        fmt.Println("错误链中包含 ErrNotFound")
    }

    errNotFoundNew := errors.New("not found")
    fmt.Println(errors.Is(errNotFoundNew, ErrNotFound)) // false
}

运行输出:

错误链中包含 ErrNotFound
false

因为 err 是基于 ErrNotFound 包装出来的,所以 Is 判断返回 true。

因为 errNotFoundNew 是一个新的 error,虽然错误内容与 ErrNotFound 相同,但是二者是两个独立的 error 对象,所以 Is 判断返回 false。

使用场景

if errors.Is(err, io.EOF) {
    // 处理文件结束逻辑
}
type ValidationError struct { Field string }
func (e *ValidationError) Is(target error) bool {
    t, ok := target.(*ValidationError)
    return ok && e.Field == t.Field
}
err := fmt.Errorf("layer2: %w", fmt.Errorf("layer1: %w", originalErr))
if errors.Is(err, originalErr) { // 直接检查最底层错误
    // 匹配成功
}

与 == 操作符的区别:

注意事项

小结

errors.Is 是 Go 错误处理的基石之一,它通过递归解包错误链,提供了一种安全、统一的方式来检查特定错误的存在。结合以下实践可最大化其效用:

3.3 As

简介

Golang 中的 errors.As 函数是一个用于错误处理的重要工具,它提供了一种类型安全的方式,用于检查错误树中是否存在某个特定类型的错误,并提取该类型的错误实例。

它是 Go 1.13 引入的错误处理增强功能之一,与 errors.Is 和 errors.Unwrap 共同构成了更灵活的错误处理机制。

函数签名

func As(err error, target any) bool

核心功能

errors.As 会递归遍历错误树(通过 Unwrap() 或 Unwrap() []error 方法解包错误),检查是否存在与 target 类型匹配的错误。如果找到,它会将匹配的错误值赋值给 target,并返回 true。

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

与 errors.Is 的区别:

示例代码

package main

import (
    "errors"
    "fmt"
)

// 自定义错误类型
type MyError struct {
    Code    int
    Message string
}

func (e *MyError) Error() string {
    return fmt.Sprintf("code: %d, msg: %s", e.Code, e.Message)
}

func main() {
    err := &MyError{Code: 404, Message: "Not Found"}
    wrappedErr := fmt.Errorf("wrapper: %w", err) // 包裹错误

    var myErr *MyError
    if errors.As(wrappedErr, &myErr) {
        fmt.Println("Found MyError:", myErr.Code, myErr.Message)
        // 输出: Found MyError: 404 Not Found
    }
}

在这个例子中:

使用场景

var pathErr *os.PathError
if errors.As(err, &pathErr) {
    fmt.Println("Failed at path:", pathErr.Path)
}

注意事项

var target *MyError        // 正确(具体类型指针)
var target error = &MyError{} // 正确(接口类型指针)

小结

errors.As 是 Go 错误处理中类型断言的最佳实践,它简化了从错误链中提取特定类型错误的操作。结合 errors.Is 和错误包裹(fmt.Errorf + %w),可以构建清晰、可维护的错误处理逻辑。

3.4 Unwrap

简介

errors.Unwrap 是 Go 1.13 引入的错误处理函数,用于获取被包装(wrapped)错误的原始错误。它是 Go 错误处理机制中错误链(error chain)支持的核心部分。

函数签名

func Unwrap(err error) error

核心功能

使用示例

err := fmt.Errorf("wrapper: %w", io.EOF)

unwrapped := errors.Unwrap(err)
fmt.Println(unwrapped == io.EOF) // 输出: true
fmt.Println(unwrapped)          // 输出: EOF
errors.Unwrap 通常与 fmt.Errorf 的 %w 动词配合使用:
func process() error {
    if err := step1(); err != nil {
        return fmt.Errorf("step1 failed: %w", err)
    }
    // ...
}

err := process()
if errors.Unwrap(err) != nil {
    // 处理原始错误
}

使用场景

currentErr := err
for currentErr != nil {
    fmt.Println(currentErr)
    currentErr = errors.Unwrap(currentErr)
}

注意事项

小结

errors.Unwrap 是处理错误链的基础工具,适用于需要手动逐层解包错误的场景。结合 errors.Is 和 errors.As 可以实现更高效和安全的错误检查。在实际开发中,优先使用 errors.Is 和 errors.As 来操作错误链,仅在需要直接访问特定层级错误时使用 errors.Unwrap。

3.5 Join

简介

Golang 中的 errors.Join 函数是 Go 1.20 版本引入的一个错误处理工具,用于将多个错误合并为一个包装错误(wrapped error)。

它特别适用于需要同时处理多个错误(例如并发操作中多个协程返回错误)的场景。

函数签名

func Join(errs ...error) error

核心功能

err1 := errors.New("error 1")
err2 := errors.New("error 2")
joinedErr := errors.Join(err1, err2)
fmt.Println(joinedErr)
// 输出:
// error 1
// error 2
示例代码
package main

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

func main() {
    err1 := errors.New("file not found")

    // 创建一个 fs.PathError 错误
    pathErr := &fs.PathError{
        Op:   "open",
        Path: "/etc/passwd",
        Err:  os.ErrPermission,
    }
    err2 := fmt.Errorf("operation failed: %w", pathErr)

    // 合并多个错误
    joinedErr := errors.Join(err1, err2)

    // 打印合并后的错误信息
    fmt.Println("Joined error:")
    fmt.Println(joinedErr)

    // 检查是否包含特定错误
    if errors.Is(joinedErr, err1) {
        fmt.Println("Found 'file not found' error")
    }

    // 提取错误链中的某个类型
    var targetErr *fs.PathError
    if errors.As(joinedErr, &targetErr) {
        fmt.Println("Found PathError:", targetErr)
    }
}

运行输出:

Joined error:
file not found
operation failed: open /etc/passwd: permission denied
Found 'file not found' error
Found PathError: open /etc/passwd: permission denied

使用场景

func processTasks(tasks []Task) error {
    var wg sync.WaitGroup
    var mu sync.Mutex
    var errs []error

    for _, task := range tasks {
        wg.Add(1)
        go func(t Task) {
            defer wg.Done()
            if err := t.Run(); err != nil {
                mu.Lock()
                errs = append(errs, err)
                mu.Unlock()
            }
        }(task)
    }

    wg.Wait()
    return errors.Join(errs...)
}
func batchProcess(files []string) error {
    var errs []error
    for _, file := range files {
        if err := processFile(file); err != nil {
            errs = append(errs, fmt.Errorf("process %s: %w", file, err))
        }
    }
    return errors.Join(errs...)
}

注意事项

小结

errors.Join 提供了一种简洁的方式将多个错误合并为一个,特别适用于需要汇总多个错误信息的场景(如并发编程或批量处理)。通过结合 errors.Is 和 errors.As,可以灵活地检查或提取合并后的错误链中的特定错误。它是 Go 错误处理工具箱中的重要补充,进一步提升了错误管理的便利性。

4.小结

errors 包是 Go 语言标准库中用于错误处理的核心包,随着 Go 版本的演进,它提供了越来越强大的错误处理能力。

以下是主要功能的总结:

errors 包与标准库中的其他错误类型(如 os.PathError、net.OpError 等)配合使用,构成了 Go 强大的错误处理体系。

参考文献

pkg.go.dev/errors

到此这篇关于Golang errors包快速上手的文章就介绍到这了,更多相关Golang errors包内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文