12  错误和错误处理

12  错误和错误处理

错误大致可以分为四种类型

当编译器无法编译程序时,例如语法错误。
当程序的行为与预期不符,但没有崩溃时。例如,当单击图形用户界面中的按钮时没有任何反应。
当发生崩溃时。例如,当对错误类型的参数应用运算符时。Erlang 编程语言具有用于处理运行时错误的内置功能。运行时错误也可以通过调用 error(Reason) 来模拟。运行时错误是 error 类的异常。
当代码本身调用 exit/1 throw/1 时。生成错误是 exitthrow 类的异常。

当 Erlang 中发生异常时,评估错误表达式的进程的执行将停止。这被称为 **失败**,即执行或评估 **失败**,或者进程 **失败**、**终止** 或 **退出**。请注意,进程可以因除失败之外的其他原因而终止/退出。

终止的进程会发出带有 **退出原因** 的 **退出信号**,该原因描述了进程终止的原因。通常,有关任何错误终止的信息都会打印到终端。有关终止的更多详细信息,请参阅“进程”一章中的 进程终止

异常是 运行时错误生成错误,它们属于三种不同的类别,具有不同的起源。try 表达式可以区分不同的类别,而 catch 表达式则不能。trycatch 表达式 中有描述。

类别 起源
error 运行时错误,例如 1+a,或者调用了 error/1,2 的进程
exit 调用了 exit/1 的进程
throw 调用了 throw/1 的进程

表 12.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/3 来创建,并由 erl_error:format_exception/4 使用。
元组的第二个元素是一个字符串(字符列表),表示函数的源文件的文件名。
元组的第二个元素是源文件中发生异常或调用函数的行号(一个大于 0 的整数)。
警告

开发人员应仅将堆栈跟踪条目用于调试目的。

VM 执行尾调用优化,这不会在堆栈跟踪中添加新条目,并且还会将堆栈跟踪限制在一定深度。此外,编译器选项、优化和将来的更改可能会添加或删除堆栈跟踪条目,导致任何预期堆栈跟踪按特定顺序或包含特定项目的代码失败。

对此规则的唯一例外是类别 error,其原因是 undef,它保证包含尝试函数的 ModuleFunctionArity 作为第一个堆栈跟踪条目。

可以使用 catchtry 来防止运行时错误和其他异常导致进程终止,有关 catchtry 的信息,请参阅 表达式

进程可以监控其他进程并检测进程终止,有关详细信息,请参阅 进程

当发生运行时错误时,即 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 之外评估 throwV 是抛出的项。
system_limit 已达到系统限制。有关系统限制的信息,请参阅 效率指南

表 12.2:   退出原因

Stack 是发生错误时正在评估的函数调用堆栈,以元组 {Module,Name,Arity,ExtraInfo} 的列表形式给出,其中最近的函数调用排在最前面。在某些情况下,最近的函数调用元组可以是 {Module,Name,[Arg],ExtraInfo}