对try 异常 运行的疑问,为什么出现两种结果
郎咸武<langxianzhe@163.com> 同学在erlang-china上post了一个问题:
请注意编号为91和92两行运行结果,请问为什么会出现两种结果。
一个抛出 {error,{badmatch,5}}
另一个抛出** exception error: no match of right hand side value 4
root@ubuntu:/usr/src/otp# erl Erlang R13B04 (erts-5.7.5) [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false 88> X=1. 1 89> try (X=5) of Val ->{normal,Val} catch error:Error -> {error,Error} end. {error,{badmatch,5}} 90> try (X=1) of Val ->{normal,Val} catch error:Error -> {error,Error} end. {normal,1} 91> try (X=5) of Val ->{normal,Val} catch error:Error -> {error,Error} end. {error,{badmatch,5}} 92> try (X=4) of Val ->{normal,Val} catch error:Error -> {error,Error} end. ** exception error: no match of right hand side value 4 93> self(). <0.36.0> 94> catch try (X=4) of Val ->{normal,Val} catch error:Error -> {error,Error} end. %%这个异常是 shell捕获到了 进一步处理后的结果 {'EXIT',{{badmatch,4},[{erl_eval,expr,3}]}} 95> try (X=4) of Val ->{normal,Val} catch error:Error -> {error,Error} end. ** exception error: no match of right hand side value 4 96> self(). <0.36.0>
竟然是EXIT, 这时候shell也换了pid了, 这是怎么回事呢?
由于在shell输入的程序是公司允许的, 我们也可以从出错的stack里面看到是 erl_eval:expr函数异常了.
现在让我们对系统打patch如下:
lib/stdlib/src/erl_eval.erl
282expr({match,_,Lhs,Rhs0}, Bs0, Lf, Ef, RBs) -> 283 {value,Rhs,Bs1} = expr(Rhs0, Bs0, Lf, Ef, none), 284 case match(Lhs, Rhs, Bs1) of 285 {match,Bs} -> 286 ret_expr(Rhs, Bs, RBs); 287 nomatch -> 288 io:format("expr nomatch->pid:~p~n~p~n",[self(), Rhs]), %%添加诊断 289 erlang:raise(error, {badmatch,Rhs}, stacktrace()) 290 end;
erts/emulator/beam/bif.c
1142/**********************************************************************/ 1143/* raise an exception of given class, value and stacktrace. 1144 * 1145 * If there is an error in the argument format, 1146 * return the atom 'badarg' instead. 1147 */ 1148Eterm 1149raise_3(Process *c_p, Eterm class, Eterm value, Eterm stacktrace) { ... 1222 erts_print(ERTS_PRINT_STDOUT, NULL, "raise->proc:%T\nclass=%T\nvalue=%T\nstacktrace=%T\n", c_p->id, class, value, c_p->ftrace); /*添加诊断*/ 1223 BIF_ERROR(c_p, reason); ... }
然 后我们在运行上面的语句:
root@ubuntu:~/otp# bin/erl Erlang R13B04 (erts-5.7.5) [source] [smp:2:2] [rq:2] [async-threads:0] [kernel-poll:false] Eshell V5.7.5 (abort with ^G) 1> X=1. 1 2> try (X=5) of Val ->{normal,Val} catch error:Error -> {error,Error} end. expr nomatch->pid:<0.32.0> 5 raise->proc:<0.32.0> class=error value={badmatch,5} stacktrace=[[{erl_eval,expr,3} {error,{badmatch,5}} 3> try (X=4) of Val ->{normal,Val} catch error:Error -> {error,Error} end. expr nomatch->pid:<0.32.0> 4 raise->proc:<0.32.0> class=error value={badmatch,4} stacktrace=[[{erl_eval,expr,3}]|-000000000000000016] raise->proc:<0.32.0> class=error value={badmatch,4} stacktrace=[[{erl_eval,expr,3}]|-000000000000000016]
很奇怪的是第3句raise了2次. 让我们回到程序好好看下:
X=1. %%这行绑定了一个变量X=1 try (X=5) of Val ->{normal,Val} catch error:Error -> {error,Error} end. %%这行由于异常会试图绑定变量Error={badmatch,5}, 由于之前Error不存在, 绑定成功. try (X=4) of Val ->{normal,Val} catch error:Error -> {error,Error} end. %%这行由于异常会绑定了变量Error={badmatch,4}, 由于Error存在, 而且值是{badmatch,5},所以这时候catch就出 现异常了, 往外抛出{'EXIT',{{badmatch,4},[{erl_eval,expr,3}]}}.
这 下我们明白了, 是这个catch惹的祸了.
我们再实验下我们的分析:
root@ubuntu:~# erl Erlang R13B04 (erts-5.7.5) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.7.5 (abort with ^G) 1> X=1. 1 2> b(). X = 1 ok 3> try (X=5) of Val ->{normal,Val} catch error:Error -> {error,Error} end. {error,{badmatch,5}} 4> b(). Error = {badmatch,5} X = 1 ok 5> try (X=4) of Val ->{normal,Val} catch error:Error -> {error,Error} end. ** exception error: no match of right hand side value 4 6> f(Error). ok 7> b(). X = 1 ok 8> try (X=4) of Val ->{normal,Val} catch error:Error -> {error,Error} end. {error,{badmatch,4}} 9>
Bingo成功.
结论: 要非常小心Erlang语法的变量绑定,在不同的路线会有不同的绑定,容易出问题.
Recent Comments