Logo
Section 1 / 10

抛出异常

学习如何在 Elixir 中处理和实现异常。

导入

抛出异常

学习如何在 Elixir 中处理和实现异常。

Elixir(如同 Erlang)认为,错误通常应导致发生错误的进程终止。一个典型的 Elixir 应用程序设计包含大量进程,这意味着错误的影响会被限制在局部范围内。监督者会检测到失败的进程,并在该层级处理重启。

因此,在 Elixir 程序中我们很少看到异常处理代码。异常会被抛出,但我们很少去捕获它们。
仅在真正异常的情况下使用异常——即那些本不该发生的情况。

实现

我们可以使用 raise 函数来抛出异常。
最简单的方式是传入一个字符串,它会生成类型为 RuntimeError 的异常。

1iex> raise "Giving up" 2** (RuntimeError) Giving up 3 erl_eval.erl:572: :erl_eval.do_apply/6

我们也可以指定异常类型以及一些可选字段。所有异常至少都实现 message 字段。

1iex> raise RuntimeError 2** (RuntimeError) runtime error 3erl_eval.erl:572: :erl_eval.do_apply/6 4 5iex> raise RuntimeError, message: "override message" 6** (RuntimeError) override message 7 erl_eval.erl:572: :erl_eval.do_apply/6

我们可以使用 try 函数来捕获异常。它接受一段要执行的代码块,以及可选的 rescue、catch 和 after 子句。

rescue 和 catch 子句的写法类似于 case 语句的主体。它们接受模式和匹配成功时要执行的代码。模式匹配的目标是被抛出的异常。

下面是一个异常处理的实际示例。我们定义了一个模块,其中包含公共函数 start。根据参数值的不同,它会调用不同的辅助函数。当参数为 0 时,运行正常;当参数为 1、2 或 3 时,会触发虚拟机抛出错误,我们捕获并报告这些错误。

我们定义了三种不同的异常模式:

  • 第一个匹配 FunctionClauseError 或 RuntimeError 这两种异常之一。
  • 第二个匹配 ArithmeticError,并将异常值存入变量 error。
  • 最后一个子句捕获任何其他异常,并将其存入变量 other_error。

我们还添加了一个 after 子句。无论是否抛出异常,这个代码块都会在 try 执行结束时始终运行。

最后,我们来看 ArithmeticError 的处理方式。除了报告错误外,我们还调用了 reraise。这会重新抛出当前异常,但允许我们添加一条新消息。同时,我们还将原始异常抛出时的堆栈跟踪一并传递过去。

1iex> c("exception.ex") 2[Boom] 3 4iex> Boom.start 1 5About to divide by zero 6%ArithmeticError{} 7Uh-oh! Arithmetic error 8DONE! 9** (RuntimeError) too late, we're doomed 10 exception.ex:26: Boom.raise_error/1 11 exception.ex:5: Boom.start/1 12 13iex> Boom.start 2 14About to call a function that doesn't exist no function match or runtime error 15DONE! 16:ok 17 18iex> Boom.start 3 19About to try creating a directory with no permission 20Disaster! %File.Error{action: "make directory", path: "/not_allowed", 21 reason: :eacces} 22DONE! 23:ok

运行上述代码片段中的命令以执行以下代码:

No configuration found for ID: e0f0e91c47b7403749c1cc78d287599d0c1b14396f45558b8021e5a93518c24c

更多酷炫内容

捕获、退出和抛出