GoSuda

为什么 Go 没有 Try-Catch?

By Rabbit Princess
views ...

Go 语言有意不提供 try-catch 支持,而仅支持 panic-recover 语法。这引起了众多习惯于使用 try-catch 进行错误处理的开发者的不满。那么,不加入 try-catch 的原因是什么呢?那是因为 try-catch 本身存在诸多问题。

try-catch-finally

try-catch 语句是为了处理程序执行过程中(运行时)可能发生的错误情况和异常情况而设的。此外,finally 语句用于编写无论是否发生异常都必须执行的代码。

错误处理与责任

在 80 年代和 90 年代,错误处理非常简单。错误消息仅限于“软盘已满”、“驱动器中没有软盘”、“没有写入软盘的权限”等,那个时代的开发者在发生错误时,会将该错误 throw 到错误处理点进行统一处理。在这种情况下,try-catch 语句可以有效地工作。

然而,随着时间的推移,情况发生了变化。业务逻辑变得复杂,出现了数据库和事务,通过网络调用大量 API 时必须解析大量的请求消息,甚至由于并发编程的出现,必须在非主线程中处理错误。

错误变得复杂到无法在一个地方处理,任何一方都无法承担所有错误的责任。在这里,try-catch 出现了严重的问题。

try-catch 的责任转嫁

简而言之,try-catch 是一种由引发错误的主体将错误发生的责任(后续处理)推卸给其他人的方法。推卸的对象可能是 catch 语句、其父方法、其父的父的父的父... 任何人。换句话说,在错误处理变得越来越多且越来越复杂的时代,try-catch 选择的方法是“总会有人来处理的”。请看以下代码。

1try {
2    data = readFile("hello.txt");
3    structuredData = parseData(data);
4    insertDBStatus(structuredData[1]);
5    startHTTPServer(structuredData[2]);
6} catch (Exception e) {
7    e.printStackTrace();
8}

上述代码的问题在于,处理 printStackTrace 的主体不明确,并且无法知道错误发生在哪个代码中。更糟糕的是,如果 try 语句中包含更多逻辑,问题会变得更加严重。但具有讽刺意味的是,随着开发变得越来越复杂,开发人员对转嫁责任的 try-catch 语句产生了依赖,他们不再考虑错误处理,责任感也逐渐淡薄,最终忘记了错误和异常处理的本质。那么,golang 是如何解决这个问题的呢?

panic, recover

Go 的优点之一是它拥有多种系统,这些系统旨在引导开发者走向正轨,成为优秀的开发者。panic - recover 也是一个例子。

表面上看,panic 和 recover 与 try-catch 没有什么不同,但不同之处在于,当出现问题时,它们不会将责任转移到外部。在 Go 中,当发生 panic 时,应该在发生 panic 的位置解决问题,而不是将该值转移到外部。这使开发者承担错误处理的责任,引导他们更深入地思考在哪里、由谁以及如何处理该错误。这既保证了用户的自主性,也为思考留下了空间。

此外,选择 panic 这个词也非常出色,与 try-recover 不同,panic-recover 仅通过词语本身就给开发者施加了压力,让他们只能在明显的错误情况下使用。开发者自然不会滥用 recover,而只会在必要的地方使用它。这对于在 Go 中编写简洁的代码非常有帮助。