当前位置: 首页>>代码示例 >>用法及示例精选 >>正文


Elixir Kernel.SpecialForms.try用法及代码示例


Elixir语言中 Kernel.SpecialForms.try 相关用法介绍如下。

用法:

try(args)
(宏)

评估给定的表达式并处理可能发生的任何错误、退出或抛出。

例子

try do
  do_something_that_may_fail(some_arg)
rescue
  ArgumentError ->
    IO.puts("Invalid argument given")
catch
  value ->
    IO.puts("Caught #{inspect(value)}")
else
  value ->
    IO.puts("Success! The result was #{inspect(value)}")
after
  IO.puts("This is printed regardless if it failed or succeeded")
end

rescue 子句用于处理异常,而catch 子句可用于捕获抛出的值和退出。 else 子句可用于根据表达式的结果控制流。 catchrescueelse 子句基于模式匹配工作(类似于 case 特殊形式)。

try/1 内部的调用不是尾递归的,因为 VM 需要保留堆栈跟踪以防发生异常。要检索堆栈跟踪,请访问 rescuecatch 子句中的 __STACKTRACE__/0

rescue 子句

除了依赖模式匹配之外,rescue 子句还为异常提供了一些便利,允许人们通过异常的名称来挽救异常。以下所有格式都是 rescue 子句中的有效模式:

# Rescue a single exception without binding the exception
# to a variable
try do
  UndefinedModule.undefined_function
rescue
  UndefinedFunctionError -> nil
end

# Rescue any of the given exception without binding
try do
  UndefinedModule.undefined_function
rescue
  [UndefinedFunctionError, ArgumentError] -> nil
end

# Rescue and bind the exception to the variable "x"
try do
  UndefinedModule.undefined_function
rescue
  x in [UndefinedFunctionError] -> nil
end

# Rescue all kinds of exceptions and bind the rescued exception
# to the variable "x"
try do
  UndefinedModule.undefined_function
rescue
  x -> nil
end

Erlang 错误

Erlang 错误在救援时会转化为 Elixir 错误:

try do
  :erlang.error(:badarg)
rescue
  ArgumentError -> :ok
end
#=> :ok

最常见的 Erlang 错误将转化为它们的 Elixir 对应错误。那些不是将被转换为更通用的 ErlangError

try do
  :erlang.error(:unknown)
rescue
  ErlangError -> :ok
end
#=> :ok

事实上, ErlangError 可用于挽救任何不是正确 Elixir 错误的错误。例如,它也可用于在转换之前挽救早期的:badarg 错误:

try do
  :erlang.error(:badarg)
rescue
  ErlangError -> :ok
end
#=> :ok

catch 子句

catch 子句可用于捕获抛出的值、退出和错误。

捕获抛出的值

catch 可用于捕获 Kernel.throw/1 抛出的值:

try do
  throw(:some_value)
catch
  thrown_value ->
    IO.puts("A value was thrown: #{inspect(thrown_value)}")
end

捕捉任何类型的值

catch 子句还支持捕获退出和错误。为此,它允许匹配捕获值的 kind 以及值本身:

try do
  exit(:shutdown)
catch
  :exit, value ->
    IO.puts("Exited with value #{inspect(value)}")
end

try do
  exit(:shutdown)
catch
  kind, value when kind in [:exit, :throw] ->
    IO.puts("Caught exit or throw with value #{inspect(value)}")
end

catch 子句还支持 :error 以及 :exit:throw,就像在 Erlang 中一样,尽管通常会避免这种情况以支持 raise /rescue 控制机制。原因之一是当捕获 :error 时,错误不会自动转换为 Elixir 错误:

try do
  :erlang.error(:badarg)
catch
  :error, :badarg -> :ok
end
#=> :ok

after 子句

after 子句允许您定义将在传递给 try/1 的代码块成功以及引发错误时调用的清理逻辑。请注意,当接收到导致其突然退出的退出信号时,该进程将照常退出,因此不能保证执行after 子句。幸运的是,Elixir 中的大多数资源(例如打开的文件、ETS 表、端口、套接字等)都链接到或监视拥有的进程,并且如果该进程退出,它们会自动清理自己。

File.write!("tmp/story.txt", "Hello, World")
try do
  do_something_with("tmp/story.txt")
after
  File.rm("tmp/story.txt")
end

else 子句

else 子句允许传递给 try/1 的主体结果在以下位置进行模式匹配:

x = 2
try do
  1 / x
rescue
  ArithmeticError ->
    :infinity
else
  y when y < 1 and y > -1 ->
    :small
  _ ->
    :large
end

如果 else 子句不存在且未引发异常,则将返回表达式的结果:

x = 1
^x =
  try do
    1 / x
  rescue
    ArithmeticError ->
      :infinity
  end

但是,如果存在 else 子句但表达式的结果与任何模式都不匹配,则会引发异常。此异常不会被同一 try 中的 catchrescue 捕获:

x = 1
try do
  try do
    1 / x
  rescue
    # The TryClauseError cannot be rescued here:
    TryClauseError ->
      :error_a
  else
    0 ->
      :small
  end
rescue
  # The TryClauseError is rescued here:
  TryClauseError ->
    :error_b
end

同样,else 子句中的异常不会在同一个 try 中被捕获或拯救:

try do
  try do
    nil
  catch
    # The exit(1) call below can not be caught here:
    :exit, _ ->
      :exit_a
  else
    _ ->
      exit(1)
  end
catch
  # The exit is caught here:
  :exit, _ ->
    :exit_b
end

这意味着 VM 不再需要在 else 子句中保留一次堆栈跟踪,因此当使用带有尾调用的 try 作为 else 子句中的最终调用时,尾递归是可能的。 rescuecatch 子句也是如此。

只有尝试过的表达式的结果才落入else 子句。如果 try 最终出现在 rescuecatch 子句中,则它们的结果不会下降到 else

try do
  throw(:catch_this)
catch
  :throw, :catch_this ->
    :it_was_caught
else
  # :it_was_caught will not fall down to this "else" clause.
  other ->
    {:else, other}
end

变量处理

由于try 中的表达式可能由于异常而未被计算,因此在try 中创建的任何变量都不能被外部访问。例如:

try do
  x = 1
  do_something_that_may_fail(same_arg)
  :ok
catch
  _, _ -> :failed
end

x
#=> unbound variable "x"

在上面的示例中,无法访问 x,因为它是在 try 子句中定义的。解决此问题的常见做法是返回在 try 中定义的变量:

x =
  try do
    x = 1
    do_something_that_may_fail(same_arg)
    x
  catch
    _, _ -> :failed
  end

相关用法


注:本文由纯净天空筛选整理自elixir-lang.org大神的英文原创作品 Kernel.SpecialForms.try(args)。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。