错误码
在C语言中,错误处理一般依赖于返回错误码的方式。比如打开文件操作,失败时返回NULL
,通过设置全局变量errno
来指示具体的错误类型。
1 |
|
1 | Error opening file: No such file or directory |
这样的话,需要开发者在每次函数调用后检查返回的错误码来判断。
但是NULL
指针原本并不是表示错误的,不看文档,并不会知道NULL
表示错误。
异常
现代编程语言中还有一些使用异常来进行错误处理。通过throw
抛出一个自定义的异常对象;使用try...catch
来捕获并处理可能抛出的异常。
1 | function divide(a, b) { |
相比于错误码,显然自定义异常可读性更强。并且代码结构更加清晰,错误处理的代码与正常代码分隔开。也就是说,错误的产生和错误的处理分离。
多线程异常
异常在多线程环境下会有些棘手,以JavaScript为例
1 | // worker.js |
1 | 5 |
执行之后,发现及时报错,终止了程序,但是异常是主线程并没有捕获到异常。
加上以下代码后,发现不再报错了
1 | // main.js |
观察命令行中也有Emitted 'error' event
,这是因为Node.js中线程间异常是通过触发error
事件。但是这个异常处理并不是try...catch
的形式。对于异步运行的代码,try...catch
还是有局限的。
类型系统
Rust中使用Result
类型来处理错误。
1 | use std::fs::File; |
可以看到main中对Result类型的模式匹配。可能返回字符串,也可能是一个I/O错误。
1 | enum Result<T, E> { |
对于多线程情况下的错误捕捉,Rust也是游刃有余
1 | use std::thread; |
而对于错误的传播,Rust中使用?
向调用者返回错误,更加方便,不过其实也是模式匹配的语法糖。
结语
本文从错误码、异常、类型系统的方式展示了错误处理的不同方式,从错误码的低可读性和地位户型,到异常处理的自定义,但是多线程情况下错误捕捉受限,最后看到类型系统对错误处理的统一。Typescript的强类型系统自然也支持类型式的错误处理。