12 错误和错误处理
12.1 术语
错误大致可以分为四种类型
- 编译时错误
- 当编译器无法编译程序时,例如语法错误。
- 逻辑错误
- 当程序的行为与预期不符,但没有崩溃时。例如,当单击图形用户界面中的按钮时没有任何反应。
- 运行时错误
- 当发生崩溃时。例如,当对错误类型的参数应用运算符时。Erlang 编程语言具有用于处理运行时错误的内置功能。运行时错误也可以通过调用 error(Reason) 来模拟。运行时错误是 error 类的异常。
- 生成错误
- 当代码本身调用 exit/1 或 throw/1 时。生成错误是 exit 或 throw 类的异常。
当 Erlang 中发生异常时,评估错误表达式的进程的执行将停止。这被称为 **失败**,即执行或评估 **失败**,或者进程 **失败**、**终止** 或 **退出**。请注意,进程可以因除失败之外的其他原因而终止/退出。
终止的进程会发出带有 **退出原因** 的 **退出信号**,该原因描述了进程终止的原因。通常,有关任何错误终止的信息都会打印到终端。有关终止的更多详细信息,请参阅“进程”一章中的 进程终止。
12.2 异常
异常是 运行时错误 或 生成错误,它们属于三种不同的类别,具有不同的起源。try 表达式可以区分不同的类别,而 catch 表达式则不能。try 和 catch 在 表达式 中有描述。
类别 | 起源 |
error | 运行时错误,例如 1+a,或者调用了 error/1,2 的进程 |
exit | 调用了 exit/1 的进程 |
throw | 调用了 throw/1 的进程 |
以上所有异常也可以通过调用 erlang:raise/3 来生成。
异常由其类别、退出原因(参见 退出原因)和堆栈跟踪(有助于查找异常的代码位置)组成。
对于任何异常类别,堆栈跟踪可以在 try 表达式中绑定到变量,或者在 catch 捕获运行时错误时作为退出原因的一部分。示例
> {'EXIT',{test,Stacktrace}} = (catch error(test)), Stacktrace. [{shell,apply_fun,3,[]}, {erl_eval,do_apply,6,[]}, ...] > try throw(test) catch Class:Reason:Stacktrace -> Stacktrace end. [{shell,apply_fun,3,[]}, {erl_eval,do_apply,6,[]}, ...]
调用堆栈回溯(堆栈跟踪)
堆栈回溯(堆栈跟踪)是一个包含 {Module, Function, Arity, ExtraInfo} 和/或 {Fun, Arity, ExtraInfo} 元组的列表。元组中的 Arity 字段可以是该函数调用的参数列表,而不是整型元数,具体取决于异常。
ExtraInfo 是一个(可能为空)的包含两个元素元组的列表,这些元组按任意顺序提供有关异常的附加信息。第一个元素是描述第二个元素中信息类型的原子。以下项目可能会出现
- error_info
- 元组的第二个元素是一个映射,提供有关导致异常原因的附加信息。此信息可以通过调用 error/3 来创建,并由 erl_error:format_exception/4 使用。
- file
- 元组的第二个元素是一个字符串(字符列表),表示函数的源文件的文件名。
- line
- 元组的第二个元素是源文件中发生异常或调用函数的行号(一个大于 0 的整数)。
开发人员应仅将堆栈跟踪条目用于调试目的。
VM 执行尾调用优化,这不会在堆栈跟踪中添加新条目,并且还会将堆栈跟踪限制在一定深度。此外,编译器选项、优化和将来的更改可能会添加或删除堆栈跟踪条目,导致任何预期堆栈跟踪按特定顺序或包含特定项目的代码失败。
对此规则的唯一例外是类别 error,其原因是 undef,它保证包含尝试函数的 Module、Function 和 Arity 作为第一个堆栈跟踪条目。
12.3 Erlang 中的运行时错误处理
进程内的错误处理
可以使用 catch 或 try 来防止运行时错误和其他异常导致进程终止,有关 catch 和 try 的信息,请参阅 表达式。
进程之间的错误处理
进程可以监控其他进程并检测进程终止,有关详细信息,请参阅 进程。
12.4 退出原因
当发生运行时错误时,即 error 类的异常。退出原因是一个元组 {Reason,Stack},其中 Reason 是一个表示错误类型的项
原因 | 错误类型 |
badarg | 错误参数。参数的数据类型错误,或者格式错误。 |
badarith | 算术表达式中的错误参数。 |
{badmatch,V} | 匹配表达式的评估失败。值 V 未匹配。 |
function_clause | 在评估函数调用时,未找到匹配的函数子句。 |
{case_clause,V} | 在评估 case 表达式时,未找到匹配的分支。值 V 未匹配。 |
if_clause | 在评估 if 表达式时,未找到真分支。 |
{try_clause,V} | 在评估 try 表达式的 of 部分时,未找到匹配的分支。值 V 未匹配。 |
undef | 在评估函数调用时,未找到该函数。 |
{badfun,F} | 函数 F 有问题。 |
{badarity,F} | 对函数应用了错误数量的参数。 F 描述了函数和参数。 |
timeout_value | receive..after 表达式中的超时值被评估为非整数或 infinity。 |
noproc | 尝试链接或监控到不存在的进程或端口。 |
noconnection | 由于无法建立或断开了节点之间的连接,因此与远程进程的链接或监控被中断。 |
{nocatch,V} | 尝试在 catch 之外评估 throw。 V 是抛出的项。 |
system_limit | 已达到系统限制。有关系统限制的信息,请参阅 效率指南。 |
Stack 是发生错误时正在评估的函数调用堆栈,以元组 {Module,Name,Arity,ExtraInfo} 的列表形式给出,其中最近的函数调用排在最前面。在某些情况下,最近的函数调用元组可以是 {Module,Name,[Arg],ExtraInfo}。