Elixir 长生不老药-尝试/捕捉vs尝试/拯救? 背景

Elixir 长生不老药-尝试/捕捉vs尝试/拯救? 背景,elixir,Elixir,try/rescue和try/catch都是Elixir中的错误处理技术。根据介绍指南中的说明 可以使用try/rescue构造来挽救错误 另一方面, throw和catch保留用于无法检索值的情况,除非使用throw和catch 怀疑 我有一个简单的理解,rescue是针对错误的。而catch用于任何值 但是, 什么时候应该使用Elixir中的错误处理机制 它们在细节上有什么区别 我应该如何选择一个在特定用例中使用 “除非使用throw和catch,否则无法检索值的情况”到底是什么 这是一个

try/rescue
try/catch
都是Elixir中的错误处理技术。根据介绍指南中的说明

可以使用
try/rescue
构造来挽救错误

另一方面,

throw
catch
保留用于无法检索值的情况,除非使用
throw
catch

怀疑 我有一个简单的理解,
rescue
是针对错误的。而
catch
用于任何值

但是,

  • 什么时候应该使用Elixir中的错误处理机制
  • 它们在细节上有什么区别
  • 我应该如何选择一个在特定用例中使用
  • “除非使用
    throw
    catch
    ,否则无法检索值的情况”到底是什么

    • 这是一个好问题。经过一点研究

      • 他们在细节上有什么不同

        何塞的回答是:

      主要是,您应该使用
      throw
      来控制流,并保留
      raise
      来处理错误,这些错误发生在开发人员错误或异常情况下

      在《长生不老药》中,这一区别相当理论化,但它们在理论上很重要 一些语言,如Ruby,其中使用错误/异常 由于创建异常对象和 回溯是昂贵的

      • 我应该如何选择一个在特定用例中使用
      请检查这个答案

      很快:

      升起/救援

      考虑raise/rescue是关于异常处理的(一些意外情况,如程序员错误、错误环境等)

      throw/catch

      在您预期会失败的地方非常有用。 典型的例子有:

      • 正在退出深度嵌套的递归调用:
      • 正常错误处理成本太高(可能发生在太多 地点:
      • 您有一个非本地构造(如事务):
      最后一点:

      • “除非使用抛出和捕获,否则无法检索值的情况”到底是什么
      假设您正试图从一个由
      主管监督的进程运行某些代码,但该进程由于意外原因而死亡

      try do
      IO.inspect MayRaiseGenServer.maybe_will_raise
      rescue
        RuntimeError -> IO.puts "there was an error"
      end
      
      MayRaiseGenServer
      Supervisor
      监督,由于某种原因引发了错误:

      try do
      IO.inspect MayRaiseGenServer.maybe_will_raise # <- Code after this line is no longer executed
      

      好的。希望这能充分说明我们在寻找什么。

      其他答案已经涵盖了
      raise
      throw
      的用法

      我将描述如何使用表格处理每个异常情况的机制:

      creating | handling with  | where y is
      -----------------------------------------------------
      raise x  | rescue y       | %RuntimeError{message: x}
      error(x) | rescue y       | %ErlangError{original: x}
      throw x  | catch y        | x
      exit(x)  | catch :exit, y | x
      
      其中
      error(x)
      实际上是
      :erlang.error(x)

      除此之外,
      rescue
      catch/1
      (catch-with-1参数)都只是一个语法糖。上述4种情况均可通过
      catch/2
      处理:

      creating | handling with | where y is | and z is
      -----------------------------------------------------------------
      raise x  | catch y, z    | :error     | %RuntimeError{message: x}
      error(x) | catch y, z    | :error     | x
      throw x  | catch y, z    | :throw     | x
      exit(x)  | catch y, z    | :exit      | x
      

      注意
      rescue
      catch/2
      处理
      raise
      error
      的不对称性:
      x
      在使用
      rescue
      时被包装在
      %ErlangError
      中,但通过阅读Dimagog的答案,在,在这件事上我真的获得了很多见解。我只是分享一个个人的实际例子

      chset = 
        %SomeModel{}
        |> SomeModel.changeset(attrs)
      try do 
        chset
        |> Repo.insert()
      catch :error,  %Postgrex.Error{postgres: %{code: :invalid_password}} ->
        { :error ,
          chset
          |> Changeset.add_error(:username, "may be invalid")
          |> Changeset.add_error(:password, "may be invalid")
        }
      else    
        {:ok, lr} -> {:ok, Map.put(lr, :password, nil)}
        error -> error
      end 
      
      postgresql错误代码来自一个
      plpgsql
      函数,我在该函数中引发了一个错误,如下所示:

       raise invalid_password using
         message = 'Invalid username or password' , 
         detail = 'A user could not be found that matched the supplied username and password';
      

      我喜欢用这个比喻:

      您可以
      接住投出的球,或者
      从山上营救某人

      • catch
        -预计用于控制流(例如类似Java的错误处理)
      • rescue
        -针对意外错误(例如运行时错误)

      您根本不应该使用它们;)您不想
      尝试/捕获所有错误的Java。当出现错误时,你应该使用监督员使你的应用程序健壮,或者在出现错误时使用raise。除了可接受的答案外,表中的可能重复项非常好。显示所有案例的表非常棒。我一直对援救/捕获以及您可以使用catch/1或catch/2感到困惑。这就清楚了。谢谢是的,这确实是一个很好而且非常简洁的回答。谢谢这张桌子很有用。我认为官方指南中关于这个话题的章节相当混乱。重要的一点是,一切都可以归结为
      catch/2
      。它帮助我处理在数据库中引发的异常。
       raise invalid_password using
         message = 'Invalid username or password' , 
         detail = 'A user could not be found that matched the supplied username and password';