目前为止,我们并没有过多的牵扯到erlang中的错误处理机制,这是由于Erlang中主要包含两个主要方面:函数和并发;目前为止,我们都是介绍的函数的内容,而对于并发(actors, 成千上万的并发进程,监察树等)的内容并未展开;虽然函数中有很多处理错误的方法,但是如之前提到的,erlang鼓励你”let it crash”,而错误处理机制很多是在并发中使用到的。但是本章我们还是主要围绕函数中的错误处理,以期平滑地过渡到并发的内容。
异常分类
错误的分类主要有分为以下几种:
- 编译时错误(相信不难理解,也就不一一罗列了)
- 逻辑错误(此处有点废话了)
运行时错误(这才是关键啊)
- Function Clause Errors:这种错误主要是由于没有满足所有的guards语句而造成的,如下:
1> lists:sort([3,2,1]). [1,2,3] 2> lists:sort(fffffff). ** exception error: no function clause matching lists:sort(fffffff) (lists.erl, line 414)
- Case Caluse Errors: 和上面类似,当没有满足符合条件的Case时抛出
- If Clause Errors: 当if语句没有指定true时抛出
Bad Match Errors: 当模式匹配失败的时候会抛出,如
5> [X,Y] = {4,5}. ** exception error: no match of right hand side value {4,5}
Bad Argument Errors:和Function Clause Errors类似,但一般是程序员自己抛出的
- Undefined Function Errors: 一般是未从module中找到相应的方法所导致
Bad Arithmetic Errors: 除0或者atom于数字相加等都会导致
8> 5 + llama. ** exception error: bad argument in an arithmetic expression in operator +/2 called as 5 + llama
Bad Function Errors: 这个最常见的是将变量作为函数使用时
Bad Arity Errors: 这个主要发生在高阶函数中,当函数调用时的入参数目大于实际定义的入参数目时抛出,如下:
10> F = fun(_) -> ok end. #Fun<erl_eval.6.13229925> 11> F(a,b). ** exception error: interpreted function with arity 1 called with two arguments
System Limit Errors: 这个比较宽泛了,涉及到进程过多、原子过长、函数入参过多、原子过多、链接节点过多等。
异常抛出
erlang中的异常主要有三种:errors, exists and throws.
- Error异常:Errors的抛出很简单,只需要调用erlang:error(Reason)即可,他就会终止你当前的进程并打印出错误栈,注意,这属于运行时异常,仅当你无法预期处理结果时抛出。
- Exit异常:Exit主要分为内部和外部exit。内部exit只需要调用exit/1就会退出当前进程,外部exit是调用exit/2,主要是用来处理多进程并发,外部退出将在之后多进程中介绍。实际上内部exit和error基本一致,区别就在于当进程结束时,会发送退出信号给其它监听进程,当使用error时,会讲错误栈等一起发送过去,而exit/1则不会。
- Throw异常:Throw异常主要是用户程序员处理的异常,调用方法也很简单,使用throw/1方法即可。通常也可以用来中断递归的调用(类似于java中的break)。
异常的处理
try Expression of
SuccessfulPattern1 [Guards] -> Expression1;
SuccessfulPattern2 [Guards] -> Expression2
catch
TypeOfError:ExceptionPattern1 -> Expression3;
TypeOfError:ExceptionPattern2 -> Expression4
after
Expression5
end.
异常处理的基本语法如上所示,其中after就相当于java中的finally,在此就不赘述了。我们可以定义一个函数来进行各种异常的试验,如下所示:
sword(1) -> throw(slice);
sword(2) -> erlang:error(cut_arm);
sword(3) -> exit(cut_leg);
sword(4) -> throw(punch);
sword(5) -> exit(cross_bridge).
black_knight(Attack) when is_function(Attack, 0) ->
try Attack() of
_ -> "None shall pass."
catch
throw:slice -> "It is but a scratch.";
error:cut_arm -> "I've had worse.";
exit:cut_leg -> "Come on you pansy!";
_:_ -> "Just a flesh wound."
end.
talk() -> "blah blah".
试验过程及结果如下所示,一目了然:
7> c(exceptions).
{ok,exceptions}
8> exceptions:talk().
"blah blah"
9> exceptions:black_knight(fun exceptions:talk/0).
"None shall pass."
10> exceptions:black_knight(fun() -> exceptions:sword(1) end).
"It is but a scratch."
11> exceptions:black_knight(fun() -> exceptions:sword(2) end).
"I've had worse."
12> exceptions:black_knight(fun() -> exceptions:sword(3) end).
"Come on you pansy!"
13> exceptions:black_knight(fun() -> exceptions:sword(4) end).
"Just a flesh wound."
14> exceptions:black_knight(fun() -> exceptions:sword(5) end).
"Just a flesh wound."
对于想在try中执行多条语句的,也十分简单,只需要用都好隔开即可
whoa() -> try
talk(),
_Knight = "None shall pass!",
_Doubles = [N*2 || N <- lists:seq(1,100)],
throw(up),
_WillReturnThis = tequila
of
tequila -> "Hey, this worked!"
catch
Exception:Reason -> {caught, Exception, Reason}
end.