"加薪"和"投掷"有什么区别?

  • 本文关键字:区别 加薪 投掷 elixir
  • 更新时间 :
  • 英文 :


在erlang中,只有throw,没有raise。 长生不老药中的raisethrow有什么区别?

错误生成的代码如下:

defp open_imu() do
{:ok, pid} = Circuits.UART.start_link()
# imu_port: "tty.usbserial-1410", 
imu_port = Application.fetch_env!(Mechanics, :imu_port)
imu_speed = Application.fetch_env!(Mechanics, :imu_speed)
case Circuits.UART.open(pid, imu_port, speed: imu_speed, active: true) do
:ok ->
pid;
{:error,reason} ->
Logger.error("serial can not open")
throw(reason) # <----- if use throw, it is ok, if use raise, it is not ok.
end
end

使用raise时,iex生成以下消息,则无法捕获。

03:12:05.921 [error] serial can not open

03:12:05.923 [error] GenServer Mechanics.MechanicsImu terminating
** (UndefinedFunctionError) function :enoent.exception/1 is undefined (module :enoent is not available)
:enoent.exception([])
(mechanics 0.1.0) lib/mechanics/mechanics_imu.ex:91: Mechanics.MechanicsImu.open_imu/0
(mechanics 0.1.0) lib/mechanics/mechanics_imu.ex:35: Mechanics.MechanicsImu.handle_info/2
(stdlib 3.17.2) gen_server.erl:695: :gen_server.try_dispatch/4
(stdlib 3.17.2) gen_server.erl:771: :gen_server.handle_msg/6
(stdlib 3.17.2) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
Last message: :check_equipment
State: %{imuPid: nil, interval: 5000}
throw

更像是流控制,可以看作是早期返回,您可以在其中将任何值类型throw到其他范围之外并停止当前函数。

raise专门用于异常,其中某些内容失败,错误。您只能raise例外,但有一些语法口红可以将字符串捆绑到异常中。

不能引发原子(除非原子是定义exception/1函数的模块的名称。

iex(local@host)1> raise :some_atom
** (UndefinedFunctionError) function :some_atom.exception/1 is undefined (module :some_atom is not available)
:some_atom.exception([])

引发一些随机数据会给出有关允许类型的更清晰的错误:

iex(local@host)1> raise %{}
** (ArgumentError) raise/1 and reraise/2 expect a module name, string or exception as the first argument, got: %{}

引发字符串是围绕运行时错误的语法糖:

iex(local@host)1> raise "runtime error shortcut"
** (RuntimeError) runtime error shortcut

直接引发运行时结构:

iex(local@host)1> raise %RuntimeError{}
** (RuntimeError) runtime error

该模块本身也可以工作:

iex(local@host)1> raise RuntimeError
** (RuntimeError) runtime error

因为从技术上讲,它正在提高这个原子:

iex(local@host)1> raise :"Elixir.RuntimeError"
** (RuntimeError) runtime error

而对位点,throw可以接受任何值:

iex(local@host)1> throw 10
** (throw) 10
iex(local@host)1> throw :atom
** (throw) :atom
iex(local@host)1> throw nil
** (throw) nil

值得指出的是,throw是"由编译器内联"的,并且比raising有更多的大脑并需要创建结构等要快得多。我认为这种差异可能是没有意义的,因为你不应该无缘无故地举起,而且通常有比投掷更好的模式,但在某些情况下,这将是有影响力的。

Generated throw_vs_raise app
Operating System: Linux
CPU Information: Intel(R) Core(TM) i5-4670K CPU @ 3.40GHz
Number of Available Cores: 4
Available memory: 23.37 GB
Elixir 1.13.1
Erlang 24.2
Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 0 ns
reduction time: 0 ns
parallel: 1
inputs: none specified
Estimated total run time: 14 s
Benchmarking raise ...
Benchmarking throw ...
Name            ips        average  deviation         median         99th %
throw        6.24 M       0.160 μs  ±4661.27%       0.156 μs       0.190 μs
raise        0.91 M        1.10 μs    ±43.02%        1.07 μs        1.48 μs
Comparison:
throw        6.24 M
raise        0.91 M - 6.84x slower +0.94 μs
Benchee.run(
%{
"throw" => fn ->
try do
throw "throw"
catch
t -> t
end
end,
"raise" => fn ->
try do
raise "raise"
rescue
r -> r
end
end})

如果足够疯狂,您可以将数据捆绑在异常中并将其用于流量控制,但不建议这样做,但到目前为止,它们都具有一些非常相似的行为,即您停止当前执行并将值传递到堆栈到其他地方,但是传递该值的内容和原因的语义是不同的。

在长生不老药中,raise用于在代码中引发异常情况,例如

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

为了捕捉提高的价值,您使用rescue,像这样

iex> try do
...>     raise "something went wrong..."
...> rescue
...>     RuntimeError -> "Got an error!"
...> end
"Got an error!"

throw的目的略有不同 - 当您(出于某种原因)必须抛出一个值以稍后捕获它时,会使用它。抛出并不一定意味着代码中发生了一些异常。它很少使用,通常是在使用未提供适当 API 的库时

要捕获抛出的值,请使用catch

iex> try do
...>     throw "my cool value"
...> catch
...>    x -> "Received #{x}"
...> end
"Received my cool value"

检查入门,一切都在那里描述

最新更新