Golang errors 包快速上手

news2025/4/22 18:34:22

文章目录

  • 1.变量
  • 2.类型
  • 3.函数
    • 3.1 New
    • 3.2 Is
      • 简介
      • 函数签名
      • 核心功能
      • 示例代码
      • 使用场景
      • 注意事项
      • 小结
    • 3.3 As
      • 简介
      • 函数签名
      • 核心功能
      • 示例代码
      • 使用场景
      • 注意事项
      • 小结
    • 3.4 Unwrap
      • 简介
      • 函数签名
      • 核心功能
      • 使用示例
      • 使用场景
      • 注意事项
      • 小结
    • 3.5 Join
      • 简介
      • 函数签名
      • 核心功能
      • 使用场景
      • 注意事项
      • 小结
  • 4.小结
  • 参考文献

在 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
  • err: 要检查的错误。
  • target: 我们想要确认的错误。
  • 返回值: 如果错误链中任一错误与目标错误相同,则返回 true。

核心功能

  1. 递归解包错误链errors.Is 会通过 Unwrap() 或Unwrap() []error方法逐层解包错误链,检查每一层错误是否与 target 匹配。
  2. 值相等性检查检查错误的“值”是否与 target 相等。默认使用 == 操作符比较,但若错误类型实现了 Is(error) bool 方法,则优先调用该方法进行判断(允许自定义相等逻辑)。
  3. 支持自定义错误匹配逻辑如果自定义错误类型需要定义特殊的相等规则(例如比较结构体字段而非指针地址),可以实现 Is(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。

使用场景

  1. 检查预定义错误例如判断错误是否为 io.EOF 或 os.ErrNotExist:
if errors.Is(err, io.EOF) {
    // 处理文件结束逻辑
}
  1. 自定义错误匹配当需要根据错误的某些字段(而非指针地址)匹配时,自定义 Is 方法:
type ValidationError struct { Field string }
func (e *ValidationError) Is(target error) bool {
    t, ok := target.(*ValidationError)
    return ok && e.Field == t.Field
}
  1. 处理多层错误链自动遍历包裹错误(如 fmt.Errorf + %w 生成的错误链):
err := fmt.Errorf("layer2: %w", fmt.Errorf("layer1: %w", originalErr))
if errors.Is(err, originalErr) { // 直接检查最底层错误
    // 匹配成功
}

与 == 操作符的区别:

  • 默认行为:如果错误类型未实现 Is 方法,errors.Is 默认使用 == 比较错误值和 target。但对于指针类型的错误(如 &MyError{}),== 比较的是指针地址而非值内容。
  • 自定义逻辑:通过实现 Is 方法,可以控制错误的匹配逻辑(如比较结构体字段)。

注意事项

  1. 优先实现 Is 方法:如果自定义错误需要支持值匹配(而非指针匹配),必须实现 Is 方法。

  2. target可以是nil:如果 target 为 nil,errors.Is 仅在 err 也为 nil 时返回 true。

  3. 性能错误链较长时,递归解包可能导致轻微性能开销,但通常可忽略。

小结

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

  • 对需要值匹配的自定义错误实现 Is 方法。
  • 优先使用 errors.Is 而非 == 直接比较错误(确保兼容错误链)。
  • 与 errors.As 分工:Is 用于值匹配,As 用于类型提取。

3.3 As

简介

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

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

函数签名

func As(err error, target any) bool
  • err: 要检查的错误树。
  • target: 一个指向目标类型的指针,用于存储结果。
  • 返回值: 如果错误树中存在指定的类型,则返回 true,并且 target 参数会被设置为相应的错误值。

核心功能

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

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

与 errors.Is 的区别:

  • errors.Is(err, target):检查错误树中是否存在值等于 target 的错误(值比较)。
  • errors.As(err, &target):检查错误树中是否存在类型与 target 匹配的错误(类型断言)。

示例代码

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

在这个例子中:

  1. 自定义错误类型 MyError 实现了 error 接口。
  2. 通过 fmt.Errorf 和 %w 包裹原始错误,形成错误树。
  3. errors.As 检查包裹后的错误树,找到 MyError 类型的错误实例,并将其赋值给 myErr。

使用场景

  1. 提取特定错误类型的详细信息当错误类型包含额外字段(如错误码、上下文信息)时,可以通过 errors.As 提取这些信息。

  2. 处理标准库中的错误类型例如,检查一个错误是否是 os.PathError 类型,以获取文件路径相关的详细信息:

var pathErr *os.PathError
if errors.As(err, &pathErr) {
    fmt.Println("Failed at path:", pathErr.Path)
}
  1. 多层级错误解包无需手动调用 Unwrap() 遍历错误链,errors.As 会自动处理嵌套错误。

注意事项

  1. target 必须是指针target 必须是一个指向接口或具体类型的指针。例如:
var target *MyError        // 正确(具体类型指针)
var target error = &MyError{} // 正确(接口类型指针)
  1. 类型必须匹配target 的类型需要与错误链中某个错误的具体类型完全一致(或接口类型)。

  2. 性能如果错误树非常长,errors.As 可能需要遍历整个树,但实际场景中性能影响通常可忽略。

小结

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
  1. 解包错误:如果 err 实现了 Unwrap() error 方法,则返回该方法的结果。
  2. 无包装时:如果错误不支持解包或已经是底层错误,返回 nil。
  3. 简单直接:仅解包一层,不会递归解包整个错误链。

核心功能

  1. 解包错误链用于从包裹错误(如通过 fmt.Errorf%w 生成的错误)中提取下一层错误。
  2. 支持自定义错误类型。若自定义错误类型实现了 Unwrap() error 方法,errors.Unwrap 可自动调用它来解包错误。

使用示例

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 {
    // 处理原始错误
}
  1. 标准接口:要求被解包的错误实现 Unwrap() error 方法
  2. 非递归:只解包一层,要解包整个错误链需要循环调用
  3. 与 errors.Is/As 配合:errors.Is 和 errors.As 内部会自动处理错误链

使用场景

  1. 逐层检查错误链通过循环调用 errors.Unwrap 遍历所有嵌套错误:
currentErr := err
for currentErr != nil {
    fmt.Println(currentErr)
    currentErr = errors.Unwrap(currentErr)
}
  1. 结合 errors.Is** 和 **errors.As虽然 errors.Is 和 errors.As 会自动遍历错误链,但在需要手动提取特定层级错误时,可用 Unwrap 配合使用。

  2. 自定义错误类型的分层处理为自定义错误实现 Unwrap() 方法,使其能融入错误链机制。

注意事项

  1. 仅解包一层每次调用 errors.Unwrap 只返回直接包裹的下层错误。需循环调用以遍历整个链。

  2. 依赖 Unwrap() 方法只有实现了 Unwrap() error 方法的错误才能被正确解包。例如:

  • fmt.Errorf 使用 %w 包裹的错误会自动实现此方法。
  • 自定义错误需显式实现 Unwrap() error。
  1. 空值处理如果 err 为 nil,或未实现 Unwrap,或 Unwrap 返回 nil,则函数返回 nil。

小结

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
  • 参数 errs …error:一个可变参数列表,接收多个 error 类型的值。
  • 返回值 如果输入的 errs 中存在至少一个非 nil 的错误,则返回一个合并后的包装错误;否则返回 nil。

核心功能

  1. 合并多个错误errors.Join 会将所有非 nil 的错误合并为一个包装错误。合并后的错误可以通过 errors.Unwrap 获取所有原始错误的切片。
  2. 兼容 errors.Is和 errors.As合并后的错误支持通过 errors.Is 和 errors.As 检查或提取其中的特定错误。例如:
  • errors.Is(err, target):如果合并后的错误链中存在与 target 匹配的错误,返回 true。
  • errors.As(err, &target):可以提取合并错误链中的第一个匹配类型的错误。
  1. 错误信息拼接合并后的错误信息是多个原始错误信息的拼接,以换行符分隔。例如:
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

使用场景

  1. 并发操作中的错误收集在多个协程并发执行时,可以使用 errors.Join 收集所有协程返回的错误:
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...)
}
  1. 批量操作中的错误汇总例如,处理多个文件或请求时,统一返回所有错误:
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...)
}
  1. 兼容已有错误处理逻辑合并后的错误仍然可以被 errors.Is 和 errors.As 处理,无需修改现有代码。

注意事项

  1. Go 版本要求errors.Join 仅在 Go 1.20 及以上版本可用。

  2. 空参数处理如果所有输入错误均为 nil,errors.Join 返回 nil。

  3. 错误解包使用 errors.Unwrap 解包合并后的错误时,会返回一个 []error 切片(包含所有非 nil 错误)。

  4. 错误顺序合并后的错误顺序与输入参数的顺序一致,但 errors.Is 和 errors.As 会按顺序检查所有错误。

小结

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

4.小结

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

以下是主要功能的总结:

  1. 基础错误创建
  • New(text string) error:创建简单的错误对象
  • 示例:err := errors.New(“file not found”)
  1. 错误检查
  • Is(err, target error) bool:检查错误链中是否包含特定错误
  • 示例:if errors.Is(err, os.ErrNotExist) {…}
  1. 错误类型提取
  • As(err error, target interface{}) bool:从错误链中提取特定类型的错误
  • 示例:var perr *fs.PathError; if errors.As(err, &perr) {…}
  1. 错误包装与解包
  • Unwrap(err error) error:解包一层错误
  • 通过 fmt.Errorf 的 %w 动词包装错误
  • 示例:wrapped := fmt.Errorf(“context: %w”, err)
  1. 错误组合
  • Join(errs …error) error(Go 1.20+):合并多个错误为一个组合错误
  • 示例:combined := errors.Join(err1, err2, err3)

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


参考文献

pkg.go.dev/errors

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2340280.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构——顺序表(C语言实现)

1.顺序表的概述 1.1 顺序表的概念及结构 在了解顺序表之前,我们要先知道线性表的概念,线性表,顾名思义,就是一个线性的且具有n个相同类型的数据元素的有限序列,常见的线性表有顺序表、链表、栈、队列、字符串等等。线…

STP原理与配置以及广播风暴实验STP实验

学习目标 环路引起的问题 掌握STP的工作原理 掌握STP的基本配置 STP的配置 环路引起的问题 一、广播风暴(Broadcast Storm) 问题原理: 交换机对广播帧(如 ARP 请求、DHCP 发现报文)的处理方式是洪泛&#xff0…

网络不可达network unreachable问题解决过程

问题:访问一个环境中的路由器172.16.1.1,发现ssh无法访问,ping发现回网络不可达 C:\Windows\System32>ping 172.16.1.1 正在 Ping 172.16.1.1 具有 32 字节的数据: 来自 172.16.81.1 的回复: 无法访问目标网。 来自 172.16.81.1 的回复:…

力扣经典拓扑排序

207. 课程表(Course Schedule) 你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。先修课程按数组 prerequisites 给出,其中 prerequisites[i] [ai, bi] ,表…

【第16届蓝桥杯C++C组】--- 2025

hello呀,小伙伴们,这是第16届蓝桥杯第二道填空题,和第一道填空题一样也是十分基础的题目,有C语言基础基本都可以解,下面我讲讲我当时自己的思路和想法,如果你们有更优化的代码和思路,也可以分享…

前端基础之《Vue(7)—生命周期》

一、什么是生命周期 1、生命周期 组件从“生”到“死”的全过程。 每一个组件都有生命周期。 2、生命周期四大阶段 创建阶段:beforeCreate、created 挂载阶段:beforeMount、mounted 更新阶段:beforeUpdate、updated 销毁阶段:be…

C语言高频面试题——指针数组和数组指针

指针数组和数组指针是 C/C 中容易混淆的两个概念,以下是详细对比: 1. 指针数组(Array of Pointers) 定义:一个数组,其元素是 指针类型。语法:type* arr[元素个数]; 例如:int* ptr_a…

Linux服务器配置Anaconda环境、Pytorch库(图文并茂的教程)

引言:为了方便后续新进组的 师弟/师妹 使用课题组的服务器,特此编文(ps:我导从教至今四年,还未招师妹) ✅ NLP 研 2 选手的学习笔记 笔者简介:Wang Linyong,NPU,2023级&a…

Android端使用无障碍服务实现远程、自动刷短视频

最近在做一个基于无障碍自动刷短视频的APP,需要支持用任意蓝牙遥控器远程控制, 把无障碍服务流程大致研究了一下,从下面3个部分做一下小结。 1、需要可调整自动上滑距离和速度以适配不同的屏幕和应用 智能适配99%机型,滑动参数可…

搭建用友U9Cloud ERP及UAP IDE环境

应用环境 Microsoft Windows 10.0.19045.5487 x64 专业工作站版 22H2Internet Information Services - 10.0.19041.4522Microsoft SQL Server 2019 - 15.0.2130.3 (X64)Microsoft SQL Server Reporing Services 2019 - 15.0.9218.715SQL Server Management Studio -18.6 laster…

多模态大语言模型arxiv论文略读(二十九)

Temporal Insight Enhancement: Mitigating Temporal Hallucination in Multimodal Large Language Models ➡️ 论文标题:Temporal Insight Enhancement: Mitigating Temporal Hallucination in Multimodal Large Language Models ➡️ 论文作者:Li Su…

卷积神经网络(CNN)详解

文章目录 引言1.卷积神经网络(CNN)的诞生背景2.卷积神经网络(CNN)介绍2.1 什么是卷积神经网络?2.2 卷积神经网络(CNN)的基本特征2.2.1 局部感知(Local Connectivity)2.2.…

【SF顺丰】顺丰开放平台API对接(注册、API测试篇)

1.注册开发者账号 注册地址:顺丰企业账户中心 2.登录开发平台 登录地址:顺丰开放平台 3.开发者对接 点击开发者对接 4.创建开发对接应用 开发者应用中“新建应用”创建应用,最多创建应用限制数量5个 注意:需要先复制保存生产校验…

VisualSVN过期后的解决方法

作为一款不错的源代码管理软件,svn还是有很多公司使用的。在vs中使用svn,大家一般用的都是VisualSVN插件。在30天试用期过后,它就不能被免费使用了。下面给大家讲如何免费延长过期时间(自定义天数,可以设定一个很大的值…

DeepSeek智能时空数据分析(二):3秒对话式搞定“等时圈”绘制

序言:时空数据分析很有用,但是GIS/时空数据库技术门槛太高 时空数据分析在优化业务运营中至关重要,然而,三大挑战仍制约其发展:技术门槛高,需融合GIS理论、SQL开发与时空数据库等多领域知识;空…

STM32学习2

一、OLED 1.1 OLED介绍 OLED(Organic Light Emitting Diode):有机发光二极管 OLED显示屏:性能优异的新型显示屏,具有功耗低、相应速度快、宽视角、轻薄柔韧等特点 0.96寸OLED模块:小巧玲珑、占用接口少…

LabVIEW液压系统远程监控与故障诊断

开发了一种基于LabVIEW的远程液压系统监控解决方案,通过先进的数据采集与分析技术,有效提升工程机械的运作效率和故障响应速度。该系统结合现场硬件设备和远程监控软件,实现了液压系统状态的实时检测和故障诊断,极大地提升了维护效…

Idea中实用设置和插件

目录 一、Idea使用插件 1.Fitten Code智能提示 2.MyBatisCodeHelperPro 3.HighlightBracketPair‌ 4.Rainbow Brackets Lite 5.GitToolBox(存在付费) 6.MavenHelperPro 7.Search In Repository 8.VisualGC(存在付费) 9.vo2dto 10.Key Promoter X 11.CodeGlance…

Java写数据结构:栈

1.概念: 一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。 压栈:栈的插…

机器学习-08-推荐算法-案例

总结 本系列是机器学习课程的系列课程,主要介绍机器学习中关联规则 参考 机器学习(三):Apriori算法(算法精讲) Apriori 算法 理论 重点 MovieLens:一个常用的电影推荐系统领域的数据集 23张图&#x…