【Go语言踩坑系列(五)】错误与异常处理

本文探讨了Go语言中的错误处理,从单返回值到多返回值、try-catch模式,展示了错误处理的演化。介绍了Go如何使用error接口以及defer、panic和recover的用法,强调了错误处理在代码中的重要性。
摘要由CSDN通过智能技术生成

声明

本系列文章并不会停留在Go语言的语法层面,更关注语言特性、学习和使用中出现的问题以及引起的一些思考。

为什么需要错误和异常处理

任何一行代码都可能存在不可预知的问题,而这些问题就是bug的根源。为了妥善处理这类问题,我们需要编写一些代码,这类代码被称为运维代码。通常情况下,我们需要发现问题、判断问题的种类、然后根据问题的种类,分别进行响应与处理。这些处理可能是写入日志、也可能是直接让代码停止运行,这些都视你的业务逻辑而定。这样一来,在我们编写了足够健壮的运维代码之后,我们便能快速的定位并解决问题。

错误处理方案的演化

单返回值

我们先看一个最原始的错误处理解决方案:Unix读取文件的API:

int open(const char *pathname, int flags);

如果成功打开这个文件,open()会返回一个int类型的文件描述符fd;如果打开失败,便会返回-1。为了正确处理该函数的错误,我们会编写以下代码:

if ((fd = open("/etc/hosts", O_RDONLY)) < 0) {
   
    printf("%s", "open failed")
    exit(1);
}

这样做会有什么问题呢?由于错误码和正确的业务逻辑混在一个返回值里,假如你忘了去判断fd的值,代码就会继续往下执行,就会把错误的-1当成正确的fd,代码就会发生不可预知的错误。除此之外,这种错误处理方式的语义也并不清晰。
那么,我们该如何解决这个问题呢?

多返回值

我们想了一下,一个返回值不行,那么搞两个返回值,把错误处理逻辑与正常逻辑区分开,代码逻辑就会变得更加清晰了。Go语言就是这样做的,我们通常能够看到如下代码:

func main() {
   
	res, err := json.Marshal(payload)
	if err != nil {
   
		return "", errors.New("序列化请求参数失败")
	}
}

通过将正确执行Marshal的返回结果与错误的返回结果分离,使代码语义更加清晰,而且这样会提醒程序员更加关注错误的返回值,而不会忘记进行错误处理。但是,这样仍然存在一个问题,会出现大量的类似代码:

if err != nil {
    // 错误处理逻辑
}

在Go语言的代码中,会出现大量对err的if判断逻辑,重复率过高,而且错误处理逻辑仍然和正常的代码逻辑混淆在一起,我们如果想进一步将错误与正常逻辑分离,该如何做呢?

try-catch

Java、PHP等语言提供了try-catch-finally的解决方案。

try {
   
    // 正常代码逻辑
} catch(\Exception $e) {
   
    // 错误处理逻辑
} finally {
   
    // 释放资源逻辑
}

try-catch彻底完成了对错误与正常代码逻辑的分离。我们用try代码块中包裹可能出现问题的代码,在catch中对这些问题代码统一进行错误处理。

资源的释放

finally代码块比较特殊,它被常常用来做一些资源及句柄的释放工作。如果没有finally,我们的代码可能会像这样:

func main() {
   
	mutex := sync.Mutex{
   }
	// 加锁
	mutex
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值