Elixir 错误

2023-12-16 20:47 更新

错误(或 异常 )用于代码中发生异常时.当试图将一个数字与原子相加,就可得到一个错误的例子:

iex> :foo + 1
** (ArithmeticError) bad argument in arithmetic expression
     :erlang.+(:foo, 1)

一个运行时错误可有​raise/1​引发:

iex> raise "oops"
** (RuntimeError) oops

其它错误可以由​raise/2​引发,通过传送错误名称和一个关键词列表作为参数:

iex> raise ArgumentError, message: "invalid argument foo"
** (ArgumentError) invalid argument foo

你也可以通过在一个模块中使用​defexception​结构来定义你自己的错误;这时你创造了一个与模块同名的错误.最常用的场景是定义一个带信息场的异常:

iex> defmodule MyError do
iex>   defexception message: "default message"
iex> end
iex> raise MyError
** (MyError) default message
iex> raise MyError, message: "custom message"
** (MyError) custom message

错误可以被解救,通过try/rescue结构:

iex> try do
...>   raise "oops"
...> rescue
...>   e in RuntimeError -> e
...> end
%RuntimeError{message: "oops"}

上述例子将运行时错误解救,并返回错误本身,然后将其打印到​iex​中.

如果错误对你毫无用处,你可以不显示它:

iex> try do
...>   raise "oops"
...> rescue
...>   RuntimeError -> "Error!"
...> end
"Error!"

实际中,Elixir开发者很少用到try/rescue结构.例如,当文件无法被打开时,许多语言会强制你解救这个错误.作为替代,Elixir中提供了File.read/1函数,其会返回一个包含文件是否被成功打开的信息的元组.

iex> File.read "hello"
{:error, :enoent}
iex> File.write "hello", "world"
:ok
iex> File.read "hello"
{:ok, "world"}

这里没有try/rescue.如果你想要处理打开文件时的不同输出,你可以简单地使用case来进行模式匹配:

iex> case File.read "hello" do
...>   {:ok, body}      -> IO.puts "Success: #{body}"
...>   {:error, reason} -> IO.puts "Error: #{reason}"
...> end

最终,打开文件时发生的错误是否为异常将由你的应用来决定.这就是Elixir为何不给File.read/1和其它许多函数强加异常.而是留给开发者来选择最好的处理方式.

当你确信一个文件存在(缺失文件确实是错误的),你可以简单地使用File.read!/1:

iex> File.read! "unknown"
** (File.Error) could not read file unknown: no such file or directory
    (elixir) lib/file.ex:305: File.read!/1

标准库中的许多函数遵循对应的异常引发模式,而非返回匹配元组.函数foo会返回{:ok, result}{:error, reason}元组,而另一个函数(foo!,同名但带有!)虽然接受与foo同样的参数,但遇到错误时会抛出异常.如果一切正常,foo!会返回(没有被元组包裹的)结果.File模块就是很好的例子.

在Elixir中,我们避免使用try/rescue,因为我们不在控制流中使用错误.我们这样解释错误:它们是预留给意料外或异常的情形的.当你需要使用控制流结构时,应该使用抛出.下面我们将讲到.


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号