当前位置: 首页 > news >正文

17 go语言(golang) - 错误处理

错误处理

错误处理是编程中用于识别、响应和恢复程序运行时出现的错误和异常情况的过程。其目的是确保程序的鲁棒性(一个系统、模型或函数在面对错误输入、工作压力、意外情况或故意攻击时仍能保持稳定性和可靠性的能力),即使在出现错误的情况下也能正常运行或优雅地终止。

而Go语言中的错误处理是一种显式的、基于值的机制,与许多其他编程语言使用异常(exception)不同。Go通过返回值来处理错误,这使得代码更加清晰和可预测。

基本概念

error接口

  • Go内置了一个名为error的接口,用于表示错误。源码:

    // The error built-in interface type is the conventional interface for
    // representing an error condition, with the nil value representing no error.
    // 内置的error接口类型是表示错误条件的常规接口,其nil值代表没有错误。
    type error interface {Error() string
    }
    
  • 任何实现了这个接口的方法都可以作为一个错误对象。

自定义error

  • 使用errors.Newfmt.Errorf()创建简单的文本描述性错误。

    func Test1(t *testing.T) {err1 := errors.New("自定义错误1")fmt.Println(err1.Error())err2 := fmt.Errorf("自定义错误2")fmt.Println(err2)
    }
    

    输出

    自定义错误1
    自定义错误2
    
  • 如果需要更复杂或结构化的信息,可以定义自己的类型并实现Error()方法。

    type MyError struct {code intmsg  string
    }func (m *MyError) Error() string {return fmt.Sprintf("code:%d,msg:%s", m.code, m.msg)
    }func Test2(t *testing.T) {myError := MyError{404,"自定义错误!",}fmt.Println(myError.Error())fmt.Println(myError)}
    

    输出

    code:404,msg:自定义错误!
    {404 自定义错误!}
    

函数返回值

  • 在Go中,函数通常会返回两个值:结果和一个表示可能发生的错误。

    func testDivide(a float64, b float64) (float64, error) {if b == 0 {return 0, fmt.Errorf("分母不能为0")} else {return a / b, nil}}
    

检查和处理错误

  • 调用函数时,需要检查第二个返回值是否为非nil,以判断是否发生了错误。
func Test3(t *testing.T) {var a float64 = 1var b float64divide, err := testDivide(a, b)if err != nil {fmt.Println(divide, err.Error())} else {fmt.Printf("结果为:%.2f\n", divide)}}

输出

0 分母不能为0

错误包装与解包

  • Go1.13引入了对error wrapping的支持,通过使用 fmt.Errorf()%w 格式化动词,可以将上下文信息附加到原始错误上。

  • 使用 errors.Unwrap() 可以提取被包装过的一层的错误。

  • errors.Is函数用于检查一个错误是否与目标错误相同,或者是否是目标错误的一部分。

func Test4(t *testing.T) {err1 := errors.New("第一层错误")err2 := fmt.Errorf("第二层:%w", err1)fmt.Println(err1)fmt.Println(err2)// errors.Is函数用于检查一个错误是否与目标错误相同,或者是否是目标错误的一部分。// 注意参数顺序,检查第一个错误是不是第二个错误的子错误,或是否同一个错误bool1 := errors.Is(err1, err2) // 返回falsefmt.Println(bool1)bool2 := errors.Is(err2, err1)fmt.Println(bool2)err3 := errors.Unwrap(err2)fmt.Println(err3)
}

输出:

第一层错误
第二层:第一层错误
false
true
第一层错误

错误类型断言与比较

  • 使用类型断言来判断特定类型的自定义异常,并访问其内部字段。
  • (推荐)errors.As函数用于检查一个错误链中是否存在某个特定类型的错误,并且可以将该错误赋值给目标变量。它不仅适用于简单的类型断言,还能处理被包装过的复杂错误结构。
func Test5(t *testing.T) {myError := MyError{code: 400,msg:  "自定义错误",}var myErr error = &myError// 假设我们只知道这是个错误,但不知道具体的类型时,可以这样处理if err, ok := myErr.(*MyError); ok {fmt.Println(err.code, err.msg)}// 但是官方还是推荐我们使用as来判断具体的错误类型var targetErr *MyErrorif errors.As(myErr, &targetErr) {fmt.Println(targetErr.code, targetErr.msg)}}

输出

400 自定义错误
400 自定义错误

断言只能判断是否那个类型,而error.As能查找是否该类型或子类型

func Test6(t *testing.T) {myError := MyError{code: 400,msg:  "自定义错误",}var myErr error = &myError// 再封装一层var myErrNew = fmt.Errorf("封装多一层:%w", myErr)// 假设我们只知道这是个错误,但不知道具体的类型时,可以这样处理if err, ok := myErrNew.(*MyError); ok {fmt.Println("1:", err.code, err.msg)}// 但是官方还是推荐我们使用as来判断具体的错误类型var targetErr *MyErrorif errors.As(myErrNew, &targetErr) {fmt.Println("2:", targetErr.code, targetErr.msg)}}

输出

2: 400 自定义错误

panic和recover

panic

panic是Go语言中的一种内建机制,用于表示程序遇到了无法恢复的严重错误,导致程序的正常执行流程被中断。它类似于其他编程语言中的异常,但在Go中主要用于不可恢复的错误情况。虽然不推荐在正常业务逻辑中使用 panic,但在一些无法恢复或者必须立即停止程序执行情况下可以考虑,比如数组越界等。

关键特点

  1. 立即停止控制流

    • panic被调用时,程序会立即停止当前函数的执行,并开始运行任何已注册的defer函数。
  2. 向上传播

    • panic会沿着调用栈向上传播,逐层退出每个调用函数,并在每个退出点运行相应的defer语句。
  3. 终止程序

    • 如果没有捕获到并处理这个panic(即没有使用recover),最终会导致整个程序崩溃并打印出堆栈跟踪信息。

使用场景

  • 通常用于那些不应该发生、且无法继续安全运行下去的问题,例如数组越界、空指针解引用等。
  • 不建议在普通业务逻辑中使用,而是保留给真正需要立即停止执行的问题。
func Test1(t *testing.T) {defer fmt.Println("执行defer")fmt.Println("程序正常执行")panic("遇到错误!")fmt.Println("程序继续执行")}

输出

=== RUN   Test1
程序正常执行
执行defer
--- FAIL: Test1 (0.00s)
panic: 遇到错误! [recovered]panic: 遇到错误!goroutine 6 [running]:
testing.tRunner.func1.2({0xa25ade0, 0xa284890}).........

recover

recover是Go语言中用于处理panic的一种机制。它允许程序在发生panic后恢复执行,从而避免程序崩溃。通常与defer一起使用,以确保即使在函数发生恐慌时也能执行一些清理操作。

基本用法

  • 捕获panic(恐慌):当一个函数调用了 panic() 时,程序会立即停止当前函数的执行,并开始沿着调用栈向上传播,直到遇到一个能够处理该panic的地方。如果在传播过程中遇到了一个包含 recover() 的延迟(deferred)函数调用,那么可以通过 recover() 捕获这个恐慌。
  • 返回值:如果成功捕获到一个正在传播中的恐慌,recover() 会返回传递给该 panic() 调用的值;如果没有处于panic状态,则返回 nil。

使用场景

  • 防止程序崩溃:通过捕获和处理不可预见或致命错误,使得程序能够继续运行或进行适当降级。
  • 清理资源:确保无论是否出现错误,都能正确释放资源(如文件句柄、网络连接等)。
func Test2(t *testing.T) {defer func() {a := recover()if a != nil {fmt.Println("recover捕获到panic:", a)} else {fmt.Println("recover没有捕获到panic")}}()fmt.Println("程序正常执行")panic("遇到错误!")fmt.Println("程序继续执行")}

输出:

程序正常执行
recover捕获到panic: 遇到错误!

和throw相似

学过其他语言特别是java的同学应该发现,这与Java中的throw关键字非常相似。

尽管panicthrow在概念上相似,但它们在各自的语言中有着不同的使用习惯和语义。在Go中,panic通常用于处理真正的异常情况,比如程序的内部错误,而错误处理则通过返回error类型来完成。在Java中,throw关键字则用于更广泛的场景,包括业务逻辑中预期的异常情况。

  1. 主动抛出异常:在Go中使用panic和在Java中使用throw都可以主动抛出一个异常或错误条件。

  2. 改变程序控制流:两者都会导致程序的正常控制流被改变。在Go中,panic会立即停止当前函数的执行,并开始执行defer语句,然后向上返回,直到被recover捕获或者程序终止。而在Java中,如果没有在该方法内部捕获,则需要由上层调用者负责处理,否则可能会导致程序崩溃。

  3. 异常传播:在两个语言中,如果抛出的异常没有被立即捕获,它会继续向上传播到调用栈中的更高层,直到被捕获或者导致程序终止。

  4. 用于不可恢复的错误panicthrow都用于表示那些不应该被忽略的严重错误,它们通常用于指示程序遇到了不可恢复的状态。

  5. 停止执行后续代码:在panicthrow之后的代码将不会被执行,直到异常被处理。

  6. 堆栈跟踪:当panic发生时,Go会打印出堆栈跟踪信息,类似于Java中throw异常时的行为,这有助于开发者定位错误发生的位置。

相关文章:

17 go语言(golang) - 错误处理

错误处理 错误处理是编程中用于识别、响应和恢复程序运行时出现的错误和异常情况的过程。其目的是确保程序的鲁棒性(一个系统、模型或函数在面对错误输入、工作压力、意外情况或故意攻击时仍能保持稳定性和可靠性的能力),即使在出现错误的情…...

PG 库停库超时异常案例

文章目录 现象官方文档停库底层流程:恢复脚本优化思路总结 现象 停库超时 <2024-11-29 12:50:43.022 UTC 87472 192.167.60.1(54862) PostgreSQL JDBC Driver postgres stk>FATAL: terminating connection due to administrator command <2024-11-29 12:50:43.022 …...

redis下载、基础数据类型、操作讲解说明,持久化、springboot整合等

1 Redis是什么 官网&#xff1a;https://redis.io 开发者&#xff1a;Antirez Redis诞生于2009年全称是Remote Dictionary Server 远程词典服务器&#xff0c;是一个基于内存的键值型NoSQL数据库。 Redis是一个开源的、高性能的键值对存储系统&#xff0c;它支持多种数据结构&…...

[代码随想录06]哈希表的使用,有效字母异位词,两数组交集,快乐数,两数之和

前言 哈希表是什么&#xff1f;一句话带你理解&#xff0c;简单来说我们对于杂乱的数据&#xff0c;怎么快速找到数据&#xff0c;如何做呢&#xff1f;一般的做法就是遍历复杂度为o(N)去找寻一个数据&#xff0c;但是吧&#xff0c;我们这样思考的话&#xff0c;还是花了大量时…...

【CSS】一篇掌握CSS

不是因为有了希望才去坚持,而是坚持了才有了希望 目录 一.导入方式 1.行内样式 2.内部样式 3.外部样式(常用) 二.选择器 1.基本选择器(常用) 1.1标签选择器 1.2类选择器 1.3id选择器 2.层次选择器 2.1后代选择器 2.2子选择器 2.3相邻兄弟选择器 2.4通用兄弟选择器…...

分层图最短路

常见情形&#xff1a; 对于边有k次操作的题。。 整体思想&#xff1a; 分层图最短路可以视作是dijkstra的一个扩展&#xff0c;通常用于处理N小于10000&#xff0c;或者是k不大的情形。整体有点类似于拆点。将一个点拆成k个点处理。层与层之间互不影响。 好了我就说这么多&…...

vue3 基本使用

Vue 3 提供了多种方式来构建用户界面&#xff0c;包括选项式 API 和 Composition API。下面我将详细介绍 Vue 3 的基本使用和语法&#xff0c;主要集中在选项式 API 上&#xff0c;因为这对于初学者来说更容易上手。 1. 创建 Vue 项目 如果你还没有一个 Vue 项目&#xff0c;…...

【maven-4】IDEA 配置本地 Maven 及如何使用 Maven 创建 Java 工程

IntelliJ IDEA&#xff08;以下简称 IDEA&#xff09;是一款功能强大的集成开发环境&#xff0c;广泛应用于 Java 开发。下面将详细介绍如何在 IDEA 中配置本地 Maven&#xff0c;并创建一个 Maven Java 工程&#xff0c;快速上手并高效使用 Maven 进行 Java 开发。 1. Maven …...

种花问题算法

假设有一个很长的花坛&#xff0c;一部分地块种植了花&#xff0c;另一部分却没有。可是&#xff0c;花不能种植在相邻的地块上&#xff0c;它们会争夺水源&#xff0c;两者都会死去。 给你一个整数数组 flowerbed 表示花坛&#xff0c;由若干 0 和 1 组成&#xff0c;其中 0 …...

对于大规模的淘宝API接口数据,有什么高效的处理方法?

1.数据分批处理 原理&#xff1a;当处理大规模数据时&#xff0c;一次性将所有数据加载到内存中可能会导致内存溢出。将数据分成较小的批次进行处理可以有效避免这个问题。示例代码&#xff1a;假设通过淘宝 API 获取到了一个包含大量商品详情的 JSON 数据列表&#xff0c;每个…...

openharmony 使用uvc库获取摄像头数据使用nativewindow显示

界面代码&#xff1a; XComponent({ id: xcomponentId, type: texture, libraryname: entry }).width(800).height(500) Natvie代码&#xff1a; 1、头文件 //NativeWindow #include <ace/xcomponent/native_interface_xcomponent.h> #include <cstdint> #incl…...

SQL Server 实战 - 多种连接

目录 背景 一、多种连接 1. 复合连接条件 2. 跨数据库连接 3. 隐连接 4. 自连接 5. 多表外连接 6. UNION ALL 二、一个对比例子 背景 本专栏文章以 SAP 实施顾问在实施项目中需要掌握的 sql 语句为偏向进行选题&#xff1a; 用例&#xff1a;SAP B1 的数据库工具&am…...

【手术显微镜】市场高度集中,由于高端手术显微镜的制造技术主要掌握于欧美企业

摘要 HengCe (恒策咨询&#xff09;是全球知名的大型咨询机构&#xff0c;长期专注于各行业细分市场的调研。行业层面&#xff0c;重点关注可能存在“卡脖子”的高科技细分领域。企业层面&#xff0c;重点关注在国际和国内市场在规模和技术等层面具有代表性的企业&#xff0c;…...

IDEA 2024 配置Maven

Step 1:确定下载Apache Maven版本 在IDEA 2024中&#xff0c;随便新建一个Maven项目&#xff1b; 在File下拉菜单栏中&#xff0c;找到Setings&#xff1b; 在Build&#xff0c;Execution&#xff0c;Deployment中找到Maven 确定下载的Apache Maven版本应略低于或等于IDEA绑…...

Admin.NET框架使用宝塔面板部署步骤

文章目录 Admin.NET框架使用宝塔面板部署步骤&#x1f381;框架介绍部署步骤1.Centos7 部署宝塔面板2.部署Admin.NET后端3.部署前端Web4.访问前端页面 Admin.NET框架使用宝塔面板部署步骤 &#x1f381;框架介绍 Admin.NET 是基于 .NET6 (Furion/SqlSugar) 实现的通用权限开发…...

Flutter中的Future和Stream

在 Flutter 中&#xff0c;Future 和 Stream 都是用于处理异步操作的类&#xff0c;它们都基于 Dart 的异步编程模型&#xff0c;但是它们的使用场景和工作方式有所不同。以下是它们的区别以及各自适用的场景。 目录 一、Future1、基本使用2、异常处理1. catchError2. onError…...

107.【C语言】数据结构之二叉树求总节点和第K层节点的个数

目录 1.求二叉树总的节点的个数 1.容易想到的方法 代码 缺陷 思考:能否在TreeSize函数内定义静态变量解决size的问题呢? 其他写法 运行结果 2.最好的方法:分而治之 代码 运行结果 2.求二叉树第K层节点的个数 错误代码 运行结果 修正 运行结果 其他写法 1.求二…...

spring boot支持那些开发工具?

Spring Boot 支持多种开发工具&#xff0c;以帮助开发者更高效地进行应用开发。以下是小编给大家分享几种常用的开发工具及其特点&#xff1a; IntelliJ IDEA&#xff1a; IntelliJ IDEA 是一款非常流行的 Java IDE&#xff0c;它提供了对 Spring Boot 的全面支持&#xff0c;…...

Go-MediatR:Go语言中的中介者模式

在Go语言中&#xff0c;确实存在一个与C#中的MediatR类似的组件包&#xff0c;名为Go-MediatR。 Go-MediatR是一个受.NET中MediatR库启发的Go语言实现&#xff0c;它专注于通过中介者模式简化命令查询责任分离&#xff08;CQRS&#xff09;模式的处理和在事件驱动架构中的应用…...

5.11【机器学习】

先是对图像进行划分 划分完后&#xff0c; 顺序读取文件夹&#xff0c;在文件夹里顺序读取图片&#xff0c; 卷积层又称为滤波器&#xff0c;通道是说滤波器的个数&#xff0c;黑白通道数为1&#xff0c;RGB通道个数为3 在输入层&#xff0c;对于输入层而言&#xff0c;滤波…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具

作者&#xff1a;来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗&#xff1f;了解下一期 Elasticsearch Engineer 培训的时间吧&#xff01; Elasticsearch 拥有众多新功能&#xff0c;助你为自己…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

Golang——9、反射和文件操作

反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一&#xff1a;使用Read()读取文件2.3、方式二&#xff1a;bufio读取文件2.4、方式三&#xff1a;os.ReadFile读取2.5、写…...

android RelativeLayout布局

<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...

前端开发者常用网站

Can I use网站&#xff1a;一个查询网页技术兼容性的网站 一个查询网页技术兼容性的网站Can I use&#xff1a;Can I use... Support tables for HTML5, CSS3, etc (查询浏览器对HTML5的支持情况) 权威网站&#xff1a;MDN JavaScript权威网站&#xff1a;JavaScript | MDN...