Erlang异常处理 #

一、异常概述 #

Erlang有三种异常类型:

类型 说明 触发方式
error 运行时错误 erlang:error/1
exit 进程退出 exit/1
throw 抛出异常 throw/1

二、try-catch表达式 #

2.1 基本语法 #

erlang
-module(try_basic).
-export([demo/1]).

demo(X) ->
    try dangerous_operation(X) of
        Result -> {ok, Result}
    catch
        error:Reason -> {error, Reason};
        exit:Reason -> {exit, Reason};
        throw:Reason -> {throw, Reason}
    end.

dangerous_operation(0) -> erlang:error(zero_not_allowed);
dangerous_operation(N) when N < 0 -> exit(negative_value);
dangerous_operation(N) when N > 100 -> throw(too_large);
dangerous_operation(N) -> N * 2.

2.2 简化语法 #

erlang
-module(try_simple).
-export([safe_divide/2]).

safe_divide(A, B) ->
    try A / B
    catch
        error:badarith -> {error, division_by_zero}
    end.

2.3 after子句 #

erlang
-module(try_after).
-export([with_resource/1]).

with_resource(Resource) ->
    try
        use_resource(Resource)
    after
        release_resource(Resource)
    end.

use_resource(R) -> {used, R}.
release_resource(R) -> io:format("Released: ~p~n", [R]).

2.4 完整语法 #

erlang
-module(try_complete).
-export([process/1]).

process(Data) ->
    try
        validate(Data),
        transform(Data)
    of
        Result -> {ok, Result}
    catch
        error:validation_error -> {error, invalid_data};
        error:Reason -> {error, Reason};
        throw:Reason -> {thrown, Reason}
    after
        cleanup()
    end.

validate(_) -> ok.
transform(D) -> {transformed, D}.
cleanup() -> ok.

三、异常类型 #

3.1 error #

erlang
-module(error_type).
-export([demo/0]).

demo() ->
    try
        erlang:error(my_error)
    catch
        error:Reason -> {caught_error, Reason}
    end.

内置错误类型:

错误 说明
badarg 错误参数
badarith 算术错误
badmatch 匹配失败
function_clause 函数子句不匹配
case_clause case子句不匹配
if_clause if子句不匹配
undef 函数未定义
badfun 错误函数
badarity 参数个数错误

3.2 exit #

erlang
-module(exit_type).
-export([demo/0]).

demo() ->
    try
        exit(my_exit)
    catch
        exit:Reason -> {caught_exit, Reason}
    end.

3.3 throw #

erlang
-module(throw_type).
-export([demo/0]).

demo() ->
    try
        throw(my_throw)
    catch
        throw:Reason -> {caught_throw, Reason}
    end.

3.4 捕获所有异常 #

erlang
-module(catch_all).
-export([demo/1]).

demo(Fun) ->
    try Fun()
    catch
        _:_ -> caught_something
    end.

四、捕获栈跟踪 #

4.1 获取栈跟踪 #

erlang
-module(stacktrace).
-export([demo/0]).

demo() ->
    try
        erlang:error(some_error)
    catch
        error:Reason:Stacktrace ->
            io:format("Error: ~p~n", [Reason]),
            io:format("Stacktrace: ~p~n", [Stacktrace])
    end.

4.2 格式化栈跟踪 #

erlang
-module(format_stacktrace).
-export([demo/0]).

demo() ->
    try
        level1()
    catch
        error:Reason:Stacktrace ->
            io:format("Error: ~p~n", [Reason]),
            print_stacktrace(Stacktrace)
    end.

level1() -> level2().
level2() -> level3().
level3() -> erlang:error(deep_error).

print_stacktrace(Stacktrace) ->
    lists:foreach(
        fun({Module, Function, Arity, Location}) ->
            File = proplists:get_value(file, Location, "unknown"),
            Line = proplists:get_value(line, Location, 0),
            io:format("  ~p:~p/~p at ~s:~p~n", [Module, Function, Arity, File, Line])
        end,
        Stacktrace
    ).

五、catch表达式 #

5.1 基本catch #

erlang
-module(catch_expr).
-export([demo/0]).

demo() ->
    Result = catch 1 / 0,
    io:format("Result: ~p~n", [Result]).

5.2 catch vs try-catch #

erlang
-module(catch_vs_try).
-export([with_catch/1, with_try/1]).

with_catch(Fun) ->
    case catch Fun() of
        {'EXIT', Reason} -> {error, Reason};
        Result -> {ok, Result}
    end.

with_try(Fun) ->
    try Fun()
    catch
        _:_ -> error
    end.

建议:优先使用try-catch,更清晰易读。

六、错误处理模式 #

6.1 防御式编程 #

erlang
-module(defensive).
-export([safe_divide/2, safe_head/1, safe_nth/2]).

safe_divide(_, 0) -> {error, division_by_zero};
safe_divide(A, B) -> {ok, A / B}.

safe_head([]) -> {error, empty_list};
safe_head([H | _]) -> {ok, H}.

safe_nth(N, List) when N > 0, N =< length(List) ->
    {ok, lists:nth(N, List)};
safe_nth(_, _) -> {error, index_out_of_bounds}.

6.2 Let it crash #

erlang
-module(let_it_crash).
-export([process/1]).

process(Data) ->
    Result = dangerous_operation(Data),
    handle_result(Result).

dangerous_operation(Data) -> Data.
handle_result(Result) -> Result.

6.3 监督模式 #

erlang
-module(supervised).
-export([run/1]).

run(Fun) ->
    Pid = spawn_link(fun() -> worker(Fun) end),
    monitor_worker(Pid).

worker(Fun) ->
    try Fun()
    catch
        _:_ -> ok
    end.

monitor_worker(Pid) ->
    receive
        {'EXIT', Pid, normal} -> ok;
        {'EXIT', Pid, Reason} -> {error, Reason}
    end.

七、实际应用 #

7.1 文件操作 #

erlang
-module(file_ops).
-export([read_file/1, write_file/2]).

read_file(Path) ->
    try
        {ok, Binary} = file:read_file(Path),
        {ok, Binary}
    catch
        error:{badmatch, {error, Reason}} -> {error, Reason}
    end.

write_file(Path, Content) ->
    try
        ok = file:write_file(Path, Content),
        ok
    catch
        error:{badmatch, {error, Reason}} -> {error, Reason}
    end.

7.2 JSON解析 #

erlang
-module(json_parse).
-export([parse/1]).

parse(Binary) ->
    try
        {ok, Term} = decode_json(Binary),
        {ok, Term}
    catch
        error:{badmatch, error} -> {error, invalid_json};
        error:Reason -> {error, Reason}
    end.

decode_json(<<"null">>) -> {ok, null};
decode_json(<<"true">>) -> {ok, true};
decode_json(<<"false">>) -> {ok, false};
decode_json(Num) when is_binary(Num) ->
    case string:to_float(binary_to_list(Num)) of
        {Float, _} -> {ok, Float};
        {error, _} ->
            case string:to_integer(binary_to_list(Num)) of
                {Int, _} -> {ok, Int};
                error -> error
            end
    end.

7.3 HTTP请求 #

erlang
-module(http_request).
-export([get/1, post/2]).

get(Url) ->
    try
        {ok, {{_, Status, _}, _Headers, Body}} = 
            httpc:request(get, {Url, []}, [], [{body_format, binary}]),
        {ok, Status, Body}
    catch
        error:{badmatch, {error, Reason}} -> {error, Reason};
        error:Reason -> {error, Reason}
    end.

post(Url, Data) ->
    try
        {ok, {{_, Status, _}, _Headers, Body}} = 
            httpc:request(post, {Url, [], "application/json", Data}, [], [{body_format, binary}]),
        {ok, Status, Body}
    catch
        error:{badmatch, {error, Reason}} -> {error, Reason};
        error:Reason -> {error, Reason}
    end.

7.4 数据库操作 #

erlang
-module(db_ops).
-export([with_connection/2]).

with_connection(Conn, Fun) ->
    try
        Result = Fun(Conn),
        {ok, Result}
    catch
        error:Reason ->
            rollback(Conn),
            {error, Reason}
    after
        return_connection(Conn)
    end.

rollback(_Conn) -> ok.
return_connection(_Conn) -> ok.

八、错误处理最佳实践 #

8.1 明确错误类型 #

erlang
-module(error_types).
-export([process/1]).

process(Data) ->
    case validate(Data) of
        {ok, ValidData} ->
            try
                Result = transform(ValidData),
                {ok, Result}
            catch
                error:Reason -> {error, {transform_error, Reason}}
            end;
        {error, Reason} ->
            {error, {validation_error, Reason}}
    end.

validate(_) -> {ok, valid}.
transform(D) -> D.

8.2 不要过度捕获 #

erlang
-module(over_catch).
-export([bad/1, good/1]).

bad(Fun) ->
    try Fun()
    catch
        _:_ -> ok
    end.

good(Fun) ->
    try Fun()
    catch
        error:Reason -> {error, Reason}
    end.

8.3 使用after清理 #

erlang
-module(cleanup).
-export([with_file/2]).

with_file(Path, Fun) ->
    {ok, File} = file:open(Path, [read]),
    try
        Fun(File)
    after
        file:close(File)
    end.

8.4 记录错误 #

erlang
-module(error_logging).
-export([process/1]).

process(Data) ->
    try
        dangerous_operation(Data)
    catch
        error:Reason:Stacktrace ->
            logger:error("Error processing data: ~p~nStacktrace: ~p~n", 
                        [Reason, Stacktrace]),
            {error, Reason}
    end.

dangerous_operation(D) -> D.

九、总结 #

本章学习了:

  • 异常类型:error、exit、throw
  • try-catch表达式
  • 栈跟踪捕获
  • catch表达式
  • 错误处理模式
  • 实际应用案例
  • 最佳实践

准备好学习函数了吗?让我们进入下一章。

最后更新:2026-03-27