结构的透析器错误
Dialyzer error for struct
这是 Elixir 1.3 中的一个最小错误示例:
defmodule Foo do
@type t :: %__MODULE__{x: non_neg_integer}
defstruct x: 0
@spec test(t) :: t
def test(%__MODULE__{} = foo), do: test2(foo)
@spec test2(t) :: t
defp test2(%__MODULE__{} = foo), do: %__MODULE__{foo | x: 5}
end
类型检查失败:foo.ex:9: The variable _@1 can never match since previous clauses completely covered the type #{'__struct__':='Elixir.Foo', _=>_}
。
我搜索了又搜索,但终究找不到对这意味着什么或如何修复它的解释。
如果将代码简化为:
defmodule Foo do
@type t :: %__MODULE__{x: non_neg_integer}
defstruct x: 0
@spec set_x_to_5(t) :: t
def set_x_to_5(%__MODULE__{} = foo), do: %__MODULE__{foo | x: 5}
end
再反编译生成的.beam文件,得到:
set_x_to_5(#{'__struct__' := 'Elixir.Foo'} = foo@1) ->
case foo@1 of
_@1 = #{'__struct__' := 'Elixir.Foo'} -> _@1#{x := 5};
_@1 -> erlang:error({badstruct, 'Elixir.Foo', _@1})
end.
如果你仔细查看 Elixir 为 %__MODULE__{foo | x: 5}
生成的 case
语句,你会发现它包含一个永远无法匹配的分支,因为 __struct__
保证是 Foo
在那个函数里面。这是由 Elixir 生成的,因为如果您将 %Struct{map | ...}
语法与不同结构的 map
一起使用,Elixir 会抛出错误:
iex(1)> defmodule Foo, do: defstruct [:x]
iex(2)> defmodule Bar, do: defstruct [:x]
iex(3)> %Foo{%Bar{x: 1} | x: 2}
** (BadStructError) expected a struct named Foo, got: %Bar{x: 1}
要解决此问题,您可以删除 __MODULE__
部分并执行以下操作:
%{foo | x: 5}
结果是一样的,您不会收到任何警告。
这是 Elixir 1.3 中的一个最小错误示例:
defmodule Foo do
@type t :: %__MODULE__{x: non_neg_integer}
defstruct x: 0
@spec test(t) :: t
def test(%__MODULE__{} = foo), do: test2(foo)
@spec test2(t) :: t
defp test2(%__MODULE__{} = foo), do: %__MODULE__{foo | x: 5}
end
类型检查失败:foo.ex:9: The variable _@1 can never match since previous clauses completely covered the type #{'__struct__':='Elixir.Foo', _=>_}
。
我搜索了又搜索,但终究找不到对这意味着什么或如何修复它的解释。
如果将代码简化为:
defmodule Foo do
@type t :: %__MODULE__{x: non_neg_integer}
defstruct x: 0
@spec set_x_to_5(t) :: t
def set_x_to_5(%__MODULE__{} = foo), do: %__MODULE__{foo | x: 5}
end
再反编译生成的.beam文件,得到:
set_x_to_5(#{'__struct__' := 'Elixir.Foo'} = foo@1) ->
case foo@1 of
_@1 = #{'__struct__' := 'Elixir.Foo'} -> _@1#{x := 5};
_@1 -> erlang:error({badstruct, 'Elixir.Foo', _@1})
end.
如果你仔细查看 Elixir 为 %__MODULE__{foo | x: 5}
生成的 case
语句,你会发现它包含一个永远无法匹配的分支,因为 __struct__
保证是 Foo
在那个函数里面。这是由 Elixir 生成的,因为如果您将 %Struct{map | ...}
语法与不同结构的 map
一起使用,Elixir 会抛出错误:
iex(1)> defmodule Foo, do: defstruct [:x]
iex(2)> defmodule Bar, do: defstruct [:x]
iex(3)> %Foo{%Bar{x: 1} | x: 2}
** (BadStructError) expected a struct named Foo, got: %Bar{x: 1}
要解决此问题,您可以删除 __MODULE__
部分并执行以下操作:
%{foo | x: 5}
结果是一样的,您不会收到任何警告。