【go】Go 语言中 errors.Is 和 errors.As 的区别

news2025/3/16 6:48:50

Go 语言中 errors.Is 和 errors.As 的区别

核心区别概述

errors.Iserrors.As 是 Go 1.13 引入的错误处理函数,它们有着不同的用途:

  • errors.Is: 判断错误链中是否包含特定的错误值(错误相等性检查)
  • errors.As: 尝试将错误转换为特定的错误类型(错误类型转换)——基于类型断言机制/类型选择机制

详细对比

1. 功能与目的

errors.Is:

func Is(err, target error) bool
  • 检查 err 错误链中是否存在与 target 匹配的错误
  • 用于确定错误是否为某个特定的预定义错误值
  • 类似于 == 比较,但能穿透错误包装

errors.As:

func As(err error, target interface{}) bool
  • 尝试将 err 或其包装的任何错误转换为 target 指向的类型
  • 用于获取特定类型的错误以访问其方法或字段
  • 类似于类型断言 err.(T),但能穿透错误包装

2. 参数类型

errors.Is:

  • 两个参数都是 error 接口类型
  • 比较两个错误值

errors.As:

  • 第一个参数是 error 接口
  • 第二个参数是一个指向实现了 error 接口的类型的指针(通常是 *T,其中 T 实现了 error

3. 返回值含义

errors.Is:

  • 返回 true: 表示错误链中包含了目标错误
  • 返回 false: 表示未找到目标错误

errors.As:

  • 返回 true: 表示成功找到匹配的错误类型,并已将值存入 target
  • 返回 false: 表示未找到匹配的错误类型

实际应用示例

errors.Is 示例

package main

import (
    "errors"
    "fmt"
    "os"
)

func main() {
    // 使用预定义错误
    _, err := os.Open("不存在的文件.txt")
    
    // 检查是否为特定的错误值
    if errors.Is(err, os.ErrNotExist) {
        fmt.Println("文件不存在") // 这将被执行
    } else {
        fmt.Println("发生了其他错误:", err)
    }
    
    // 使用包装错误
    err = fmt.Errorf("文件操作失败: %w", os.ErrPermission)
    
    // 即使错误被包装,errors.Is 也能识别
    if errors.Is(err, os.ErrPermission) {
        fmt.Println("权限被拒绝") // 这将被执行
    } else {
        fmt.Println("发生了其他错误:", err)
    }
}

errors.As 示例

package main

import (
    "errors"
    "fmt"
    "os"
)

// 自定义错误类型
type DatabaseError struct {
    Query string
    Err   error
}

func (d *DatabaseError) Error() string {
    return fmt.Sprintf("数据库查询 %q 失败: %v", d.Query, d.Err)
}

// 实现 Unwrap 以支持错误包装
func (d *DatabaseError) Unwrap() error {
    return d.Err
}

func queryDatabase(query string) error {
    // 模拟数据库查询失败
    return &DatabaseError{
        Query: query,
        Err:   errors.New("连接超时"),
    }
}

func main() {
    err := queryDatabase("SELECT * FROM users")
    
    // 使用 errors.As 获取具体错误类型
    var dbErr *DatabaseError
    if errors.As(err, &dbErr) {
        fmt.Printf("数据库错误: 查询=%q, 原因=%v\n", dbErr.Query, dbErr.Err)
    }
    
    // 包装错误后仍能识别
    wrappedErr := fmt.Errorf("操作失败: %w", err)
    
    var pathErr *os.PathError
    if errors.As(wrappedErr, &pathErr) {
        fmt.Println("路径错误:", pathErr.Path)
    } else {
        fmt.Println("不是路径错误")
    }
    
    // 可以识别包装的原始类型
    if errors.As(wrappedErr, &dbErr) {
        fmt.Printf("在包装错误中找到数据库错误: 查询=%q\n", dbErr.Query)
    }
}

复杂场景:多层错误包装

package main

import (
    "database/sql"
    "errors"
    "fmt"
    "os"
)

type ConfigError struct {
    Field string
    Err   error
}

func (c *ConfigError) Error() string {
    return fmt.Sprintf("配置错误 (%s): %v", c.Field, c.Err)
}

func (c *ConfigError) Unwrap() error {
    return c.Err
}

func openDB() error {
    // 模拟 SQL 错误
    sqlErr := sql.ErrNoRows
    
    // 第一层包装:数据库错误
    dbErr := fmt.Errorf("数据库连接失败: %w", sqlErr)
    
    // 第二层包装:配置错误
    configErr := &ConfigError{
        Field: "database_url",
        Err:   dbErr,
    }
    
    // 第三层包装:通用操作错误
    return fmt.Errorf("无法初始化应用: %w", configErr)
}

func main() {
    err := openDB()
    
    // 使用 errors.Is 检查深层错误
    if errors.Is(err, sql.ErrNoRows) {
        fmt.Println("检测到底层 SQL 错误:没有行") // 将执行
    }
    
    // 使用 errors.As 提取特定类型
    var configErr *ConfigError
    if errors.As(err, &configErr) {
        fmt.Printf("检测到配置错误,字段: %s\n", configErr.Field) // 将执行
    }
    
    // 对比不存在的错误
    if errors.Is(err, os.ErrNotExist) {
        fmt.Println("文件不存在") // 不会执行
    }
    
    var pathErr *os.PathError
    if errors.As(err, &pathErr) {
        fmt.Println("路径错误:", pathErr.Path) // 不会执行
    }
    
    fmt.Println("原始错误:", err)
}

自定义错误比较行为

可以通过实现 IsAs 方法来自定义错误的比较行为:

package main

import (
    "errors"
    "fmt"
)

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

func (e *ErrorCode) Error() string {
    return fmt.Sprintf("[错误 %d] %s", e.Code, e.Message)
}

// 自定义 Is 方法,只比较错误码,不比较消息
func (e *ErrorCode) Is(target error) bool {
    t, ok := target.(*ErrorCode)
    if !ok {
        return false
    }
    return e.Code == t.Code
}

// 自定义 As 方法
func (e *ErrorCode) As(target interface{}) bool {
    // 特殊处理某些转换
    switch t := target.(type) {
    case **ErrorCode:
        *t = e
        return true
    default:
        return false
    }
}

var (
    ErrNotFound   = &ErrorCode{Code: 404, Message: "资源不存在"}
    ErrPermission = &ErrorCode{Code: 403, Message: "权限被拒绝"}
)

func main() {
    // 创建相同代码但不同消息的错误
    currentErr := &ErrorCode{Code: 404, Message: "用户不存在"}
    
    // 使用自定义 Is 行为
    if errors.Is(currentErr, ErrNotFound) {
        fmt.Println("是 404 错误") // 将执行,因为我们只比较错误码
    }
    
    if errors.Is(currentErr, ErrPermission) {
        fmt.Println("是 403 错误") // 不会执行
    }
    
    // 自定义 As 行为
    var targetErr *ErrorCode
    if errors.As(currentErr, &targetErr) {
        fmt.Printf("错误代码: %d, 消息: %s\n", targetErr.Code, targetErr.Message)
    }
}

何时使用哪个函数

使用 errors.Is 的场景

  1. 检查错误是否为预定义的特定错误值

    if errors.Is(err, io.EOF) { ... }
    if errors.Is(err, context.DeadlineExceeded) { ... }
    
  2. 基于错误值进行条件分支

    switch {
    case errors.Is(err, sql.ErrNoRows):
        // 处理数据不存在情况
    case errors.Is(err, context.Canceled):
        // 处理取消情况
    default:
        // 处理其他错误
    }
    

使用 errors.As 的场景

  1. 需要访问特定错误类型的字段或方法

    var netErr net.Error
    if errors.As(err, &netErr) {
        if netErr.Timeout() {
            // 处理超时特定逻辑
        }
    }
    
  2. 从错误中提取额外信息

    var syntaxErr *json.SyntaxError
    if errors.As(err, &syntaxErr) {
        fmt.Printf("JSON 语法错误在位置 %d\n", syntaxErr.Offset)
    }
    

总结比较

特性errors.Iserrors.As
用途检查错误相等性错误类型转换
与标准操作的对比== 的增强版类型断言的增强版
第二个参数类型error指向错误类型的指针
适用场景检查特定预定义错误访问特定错误类型的字段/方法
自定义行为通过实现 Is(error) bool通过实现 As(interface{}) bool

理解这两个函数的区别对于编写健壮的 Go 错误处理代码至关重要,它们允许你在保持错误包装和传递的同时,仍能进行精确的错误类型检查和处理。

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

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

相关文章

网络爬虫【简介】

我叫补三补四,很高兴见到大家,欢迎一起学习交流和进步 今天来讲一讲视图 一、网络爬虫的定义 网络爬虫(Web Crawler),又称为网络蜘蛛、网络机器人等,是一种按照一定规则自动抓取互联网信息的程序或脚本。它…

2024华东师范大学计算机复试上机真题

2024华东师范大学计算机复试机试真题 2023华东师范大学计算机复试机试真题 2022华东师范大学计算机复试机试真题 2024华东师范大学计算机复试上机真题 2023华东师范大学计算机复试上机真题 2022华东师范大学计算机复试上机真题 在线评测:传动门:pgcode…

14.使用各种读写包操作 Excel 文件:辅助模块

一 各种读写包 这些是 pandas 在底层使用的各种读写包。无须安装 pandas,直接使用这些读写包就能够读写 Excel 工作簿。可以尽可能地使用 pandas 来解决这类问题,只在 pandas 没有提供你所需要的功能时才用到读写包。 表中没有 xlwings ,因为…

Python数据分析之数据可视化

Python 数据分析重点知识点 本系列不同其他的知识点讲解,力求通过例子让新同学学习用法,帮助老同学快速回忆知识点 可视化系列: Python基础数据分析工具数据处理与分析数据可视化机器学习基础 四、数据可视化 图表类型与选择 根据数据特…

1、操作系统引论

一、操作系统 会使用linux系统 建议大家先学会linux的基础指令,可以看菜鸟教程网站进行学习。 1、各种定义 操作系统定义 管理计算机的 硬件 和软件资源, 能对各类作业进行调度,方便用户使用计算机的程序集合。操作系统运行在内核态&#xf…

HarmonyOS NEXT - 网络请求问题(http)

HTTP(HyperText Transfer Protocal,超文本传输协议)是一种用于传输超媒体文档(如HTML)的应用层协议,它是客户端和服务器之间通信的基础;无论是获取数据、提交表单、上传文件,HTTP都扮…

告别旧版本,功能全面升级!

小伙伴们,今天来给大家唠唠一款超经典的软件——格式工厂!相信很多人都不陌生吧?它可是早期超多人用的视频格式转换工具呢!但随着软件行业的发展,它慢慢被其他工具代替了,像万兴、小丸、AME这些新宠儿一出现…

Obsidian Copilot:打造你的专属 AI 笔记助手

Obsidian Copilot作为一款非常受欢迎的Obsidian插件,不仅极大地提升了用户的笔记管理和信息检索效率,还通过其多样化的AI功能为用户带来了前所未有的便捷体验。本文将详细介绍Obsidian Copilot的核心特点、使用方法及个人体验分享。 核心特点 Obsidian…

VPC4-通达oa-docker逃逸-shiro反序列化-hash传递-CrackMapExec喷射-历史ptt攻击-进程注入

由于本人是菜鸡,不会免杀,所有免杀的部分就直接跳过了 (hhh) 靶场地址: 链接: https://pan.baidu.com/s/1Fh1Zg79n1yjCPe6rrQ2apA 提取码: qiag 第一台ubuntu(docker逃逸,shiro反序列化) fscan扫到一…

C++类与对象——拷贝构造与运算符重载

拷贝构造函数和赋值运算符重载就是C类默认六个函数之二。 拷贝构造函数: 如果⼀个构造函数的第⼀个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数 也叫做拷贝构造函数,也就是说拷贝构造是⼀个特殊的构造函数…

疗养院管理系统设计与实现(代码+数据库+LW)

摘 要 传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,在计算机上安装疗养院管理系统软件来发挥其高效地信息处理的作用&#xf…

2024年12月CCF-GESP编程能力等级认证C++编程四级真题解析

四级真题的难度: 一、总体难度评价 CCF-GESP编程能力等级认证C++四级真题的难度通常被认为相对较高。它不仅要求考生具备扎实的C++编程基础,还需要考生掌握一定的算法和数据结构知识,以及良好的问题解决能力。 二、具体难度分析 ‌理论知识考察‌: 单选题和判断题中,会涉…

MySQL开发陷阱与最佳实践:第1章:MySQL开发基础概述-1.1 MySQL简介与应用场景

👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 MySQL开发陷阱与最佳实践:第1章:MySQL开发基础概述-1.1 MySQL简介与应用场景1.1.1 MySQL的发展历程与市场地位1.1.2 MySQL的核心特性与技术优势1.1.2…

使用GitHub Actions实现Git推送自动部署到服务器

将网站一键部署到服务器的方案很多,比如纯Shell脚本结合SSH、Jenkins等工具。本文将介绍如何利用GitHub Actions这一免费且轻量的CI/CD工具,实现代码推送后自动部署到云服务器。 之前一直在使用github的工作流,确实是一个比较好用的工具。 我…

PyTorch 系列教程:探索自然语言处理应用

本文旨在介绍如何使用PyTorch进行自然语言处理(NLP)的基础知识,包括必要的库、概念以及实际代码示例。通过阅读本文,您将能够开始您的NLP之旅。 1. 理解PyTorch PyTorch是一个开源的机器学习库,基于Torch库&#xff0…

3.14-1列表

列表 一.列表的介绍和定义 1 .列表 类型: <class list> 2.符号:[] 3.定义列表: 方式1:[] 通过[] 来定义 list[1,2,3,4,6] print(type(list)) #<class list> 方式2: 通过list 转换 str2"12345" print(type(str2)) #<class str> list2lis…

pyroSAR:开源的SAR数据处理与分析工具

今天为大家介绍的软件是pyroSAR&#xff1a;一款开源的SAR数据处理与分析工具。下面&#xff0c;我们将从软件的主要功能、支持的系统、软件官网等方面对其进行简单的介绍。 pyroSAR官网网址为&#xff1a;https://pyrosar.readthedocs.io/en/latest/。 pyroSAR是一个开源Pytho…

Visual Studio里的调试(debugging)功能介绍

参考 1- Introduction to Debugging | Basic Visual Studio Debugging&#xff08;这是一位印度博主视频&#xff0c;我下面做到笔记也主要参考她的视频&#xff0c;但不得不说口音太重了&#xff0c;一股咖喱味&#xff09; 目录 个人对调试浅显的认识和对调试的介绍逐行调…

图论part4|827. 最大人工岛、127. 单词接龙、463. 岛屿的周长

827. 最大人工岛 &#x1f517;&#xff1a;827. 最大人工岛 - 力扣&#xff08;LeetCode&#xff09;827. 最大人工岛 - 给你一个大小为 n x n 二进制矩阵 grid 。最多 只能将一格 0 变成 1 。返回执行此操作后&#xff0c;grid 中最大的岛屿面积是多少&#xff1f;岛屿 由一…

Java高级-05.反射的作用、应用场景

一.反射的作用 二.案例 Student.class package com.njau.d2_reflect;public class Student {private String name;private int age;private char sex;private double height;private String hobby;public Student(String name, int age, char sex, double height, String …