【go】Go 语言中 errors.Is 和 errors.As 的区别
Go 语言中 errors.Is 和 errors.As 的区别
核心区别概述
errors.Is 和 errors.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 mainimport ("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 mainimport ("errors""fmt""os"
)// 自定义错误类型
type DatabaseError struct {Query stringErr 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 *DatabaseErrorif errors.As(err, &dbErr) {fmt.Printf("数据库错误: 查询=%q, 原因=%v\n", dbErr.Query, dbErr.Err)}// 包装错误后仍能识别wrappedErr := fmt.Errorf("操作失败: %w", err)var pathErr *os.PathErrorif errors.As(wrappedErr, &pathErr) {fmt.Println("路径错误:", pathErr.Path)} else {fmt.Println("不是路径错误")}// 可以识别包装的原始类型if errors.As(wrappedErr, &dbErr) {fmt.Printf("在包装错误中找到数据库错误: 查询=%q\n", dbErr.Query)}
}
复杂场景:多层错误包装
package mainimport ("database/sql""errors""fmt""os"
)type ConfigError struct {Field stringErr 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 *ConfigErrorif errors.As(err, &configErr) {fmt.Printf("检测到配置错误,字段: %s\n", configErr.Field) // 将执行}// 对比不存在的错误if errors.Is(err, os.ErrNotExist) {fmt.Println("文件不存在") // 不会执行}var pathErr *os.PathErrorif errors.As(err, &pathErr) {fmt.Println("路径错误:", pathErr.Path) // 不会执行}fmt.Println("原始错误:", err)
}
自定义错误比较行为
可以通过实现 Is 和 As 方法来自定义错误的比较行为:
package mainimport ("errors""fmt"
)// 自定义错误类型
type ErrorCode struct {Code intMessage 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 = ereturn truedefault: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 *ErrorCodeif errors.As(currentErr, &targetErr) {fmt.Printf("错误代码: %d, 消息: %s\n", targetErr.Code, targetErr.Message)}
}
何时使用哪个函数
使用 errors.Is 的场景
-
检查错误是否为预定义的特定错误值
if errors.Is(err, io.EOF) { ... } if errors.Is(err, context.DeadlineExceeded) { ... } -
基于错误值进行条件分支
switch { case errors.Is(err, sql.ErrNoRows):// 处理数据不存在情况 case errors.Is(err, context.Canceled):// 处理取消情况 default:// 处理其他错误 }
使用 errors.As 的场景
-
需要访问特定错误类型的字段或方法
var netErr net.Error if errors.As(err, &netErr) {if netErr.Timeout() {// 处理超时特定逻辑} } -
从错误中提取额外信息
var syntaxErr *json.SyntaxError if errors.As(err, &syntaxErr) {fmt.Printf("JSON 语法错误在位置 %d\n", syntaxErr.Offset) }
总结比较
| 特性 | errors.Is | errors.As |
|---|---|---|
| 用途 | 检查错误相等性 | 错误类型转换 |
| 与标准操作的对比 | == 的增强版 | 类型断言的增强版 |
| 第二个参数类型 | error | 指向错误类型的指针 |
| 适用场景 | 检查特定预定义错误 | 访问特定错误类型的字段/方法 |
| 自定义行为 | 通过实现 Is(error) bool | 通过实现 As(interface{}) bool |
理解这两个函数的区别对于编写健壮的 Go 错误处理代码至关重要,它们允许你在保持错误包装和传递的同时,仍能进行精确的错误类型检查和处理。
相关文章:
【go】Go 语言中 errors.Is 和 errors.As 的区别
Go 语言中 errors.Is 和 errors.As 的区别 核心区别概述 errors.Is 和 errors.As 是 Go 1.13 引入的错误处理函数,它们有着不同的用途: errors.Is: 判断错误链中是否包含特定的错误值(错误相等性检查)errors.As: 尝试将错误转换…...
网络爬虫【简介】
我叫补三补四,很高兴见到大家,欢迎一起学习交流和进步 今天来讲一讲视图 一、网络爬虫的定义 网络爬虫(Web Crawler),又称为网络蜘蛛、网络机器人等,是一种按照一定规则自动抓取互联网信息的程序或脚本。它…...
JPA动态查询自定义排序规则
方法1:使用 CASE WHEN 语句排序 // 自定义排序逻辑 // 定义“离线”排在前,“在线”排在后 Expression<Object> caseExpression cb.selectCase().when(cb.equal(root.get("status"), "离线"), 0).when(cb.equal(root.get(…...
卫语句优化多层if else嵌套
一、卫语句的介绍 卫语句是一种编程实践,用于在函数或方法的开头快速处理不符合条件的情况,从而避免深层次的嵌套结构。它的核心思想是尽早返回,减少嵌套,使代码更加清晰易读。 二、卫语句的作用 提高可读性:卫语句将…...
2024华东师范大学计算机复试上机真题
2024华东师范大学计算机复试机试真题 2023华东师范大学计算机复试机试真题 2022华东师范大学计算机复试机试真题 2024华东师范大学计算机复试上机真题 2023华东师范大学计算机复试上机真题 2022华东师范大学计算机复试上机真题 在线评测:传动门:pgcode…...
3.15刷题
P6337 [COCI 2007/2008 #2] CRNE - 洛谷 #include<bits/stdc.h> using namespace std; int main(){int n;cin>>n;//横加竖 最大。n/2,n/21if(n%20){cout<<(n/21)*(n/21);}else cout<<(n/22)*(n/21);return 0; }P6338 [COCI 2007/2008 #2] PRVA - 洛…...
14.使用各种读写包操作 Excel 文件:辅助模块
一 各种读写包 这些是 pandas 在底层使用的各种读写包。无须安装 pandas,直接使用这些读写包就能够读写 Excel 工作簿。可以尽可能地使用 pandas 来解决这类问题,只在 pandas 没有提供你所需要的功能时才用到读写包。 表中没有 xlwings ,因为…...
设计心得——多态
一、设计上的多态 无论是在网上还是书籍上,还是自己的文章里都反复分析过多态的原理、应用和各种常见的情况。本篇重点从设计的角度来阐述一下多态,而不对多态的具体的用法进行说明。在前面的知识学习中可以知道,多态可以分为动多态和靜多态…...
【DeepSeek】本地部署DeepSeek的完整教程(Ollama+Docker+Open WebUI)
本地部署DeepSeek的完整教程 文章目录 本地部署DeepSeek的完整教程写在前面技术需求详细步骤一. 安装Ollama软件二. 安装DeepSeek-R1模型三. 安装Docker软件四. 配置Web UI界面问题解决1. 打开`docker desktop`时,一直显示`Docker Engine stopped`2. 用`Docker`拉取`Open WebU…...
Python数据分析之数据可视化
Python 数据分析重点知识点 本系列不同其他的知识点讲解,力求通过例子让新同学学习用法,帮助老同学快速回忆知识点 可视化系列: Python基础数据分析工具数据处理与分析数据可视化机器学习基础 四、数据可视化 图表类型与选择 根据数据特…...
1、操作系统引论
一、操作系统 会使用linux系统 建议大家先学会linux的基础指令,可以看菜鸟教程网站进行学习。 1、各种定义 操作系统定义 管理计算机的 硬件 和软件资源, 能对各类作业进行调度,方便用户使用计算机的程序集合。操作系统运行在内核态…...
DeepSeek 本地化新篇章:Ollama 兼容 OpenAI API 的深度解析与部署实践
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 随着大语言模型(LLM)的快速发展,开发者对本地化部署和 API 兼容性的需求日益增加。Ollama 作为一个轻量级开源框架,通过兼容 OpenAI AP…...
【PTA题目解答】7-4 数气球 (20分)
1.题目 天空上有n个气球,第i个气球的颜色为colori(为全由小写字母组成的字符串) 请你数出每种颜色的气球的数量,并按照颜色出现的先后顺序进行排序输出。 输入格式: 测试数据有T组(1≤T≤100)。 对于每组样例,第一…...
Swift 中 associatedtype 的用法详解
目录 前言 1.什么是associatedtype 2.associatedtype 的作用 1.让协议支持泛型 2.让协议支持不同的数据类型 3.结合 where 关键字限制类型 4.什么时候使用 associatedtype 5.总结 前言 在 Swift 语言中,泛型(Generics)是一个非常强大…...
java泛型通配符?及上下界(extends,super)保证安全性、灵活性、可读性
在 Java 中,泛型通配符(?)用于表示未知类型,通常用于增强泛型的灵活性。通配符可以与上下限结合使用,以限制泛型的范围。以下是通配符及上下限的使用示例: 1. 无界通配符 (?) 无界通配符表示可以接受任意…...
HarmonyOS NEXT - 网络请求问题(http)
HTTP(HyperText Transfer Protocal,超文本传输协议)是一种用于传输超媒体文档(如HTML)的应用层协议,它是客户端和服务器之间通信的基础;无论是获取数据、提交表单、上传文件,HTTP都扮…...
告别旧版本,功能全面升级!
小伙伴们,今天来给大家唠唠一款超经典的软件——格式工厂!相信很多人都不陌生吧?它可是早期超多人用的视频格式转换工具呢!但随着软件行业的发展,它慢慢被其他工具代替了,像万兴、小丸、AME这些新宠儿一出现…...
WSL2 Ubuntu安装GCC不同版本
WSL2 Ubuntu安装GCC不同版本 介绍安装gcc 7.1方法 1:通过源码编译安装 GCC 7.1步骤 1:安装编译依赖步骤 2:下载 GCC 7.1 源码步骤 3:配置和编译步骤 4:配置环境变量步骤 5:验证安装 方法 2:通过…...
双目视觉小记
双目视觉及其数学原理 1. 双目视觉概述 双目视觉(Stereo Vision)是一种模仿人眼视觉系统的计算机视觉技术,利用两台摄像机从不同角度拍摄同一场景,通过图像处理和三维重建来获取场景的深度信息。双目视觉广泛应用于机器人、自动…...
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扫到一…...
稳定运行的以Oracle数据库为数据源和目标的ETL性能变差时提高性能方法和步骤
要提高以 Oracle 为数据源和目标的ETL性能,需要综合考虑数据库性能优化、ETL工具配置、查询优化、并行处理和资源管理等多个方面。优化过程中要根据具体的ETL场景和工具来选择适合的方案,同时建议进行逐步调优,测试不同方案的效果,…...
C++类与对象——拷贝构造与运算符重载
拷贝构造函数和赋值运算符重载就是C类默认六个函数之二。 拷贝构造函数: 如果⼀个构造函数的第⼀个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数 也叫做拷贝构造函数,也就是说拷贝构造是⼀个特殊的构造函数…...
疗养院管理系统设计与实现(代码+数据库+LW)
摘 要 传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,在计算机上安装疗养院管理系统软件来发挥其高效地信息处理的作用…...
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库࿰…...
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…...
什么是强哈希算法pbkdf2(Password-Based Key Derivation Function)
文章目录 什么是pbkdf2使用场景 在线工具 什么是pbkdf2 维基百科:https://zh.wikipedia.org/zh-cn/PBKDF2 PBKDF2(Password-Based Key Derivation Function 2)是一种基于密码的密钥派生函数。它的主要作用是从密码和盐(salt&…...
