Elixir 如何将 Map 结构转换为 Record 结构

Elixir How to convert a Map struct to a Record struct

我有一个 Record 结构和一个 Map 结构,例如:

defmodule Foo.Bar do
  defstruct boo: nil, baz: nil
end

defmodule Foo do
  require Record
  Record.defrecord :bar, Foo.Bar, [boo: nil, baz: nil]
end

我可以像这样将 Record 转换为 Map:

defp update_map({k, v}, map), do: Map.update!(map, k, fn(_) -> v end)
defp rd2map(rd) do
  Foo.bar(rd) |> Enum.reduce(%Foo.Bar{}, &update_map/2)
end

但是如何将 Map 转换为 Record?

Elixir Records are deprecated。现在存在于 Elixir 中的 Record 模块仅用于两件事:

  1. 使用简短的内部数据
  2. 与 Erlang 记录交互

这意味着除非您尝试从 Erlang 文件中提取记录信息,否则您可能不应该使用它们。

关于您最初的问题,以下是我如何来回转换 Erlang Records 和 Elixir Structs。一旦您意识到结构只是一个包含 __struct__: Foo.BarMap,而 Record 只是一个以 {Foo.Bar, ...} 开头的元组,这就非常简单了。唯一棘手的一点是有关记录字段的信息仅在编译时可用。因此,默认情况下没有动态构建记录的方法。据我所知,您只能通过将字段定义存储在某处来解决此问题,并使用它来生成结构和记录定义。稍后,相同的源被重新用于构建具有默认值(即记录)的有序元组。请记住,您真的不应该使用记录。所以,请注意:前面有丑陋的骇客 ;-)

defmodule Foo.Bar do
  @fields [boo: nil, baz: nil]
  def fields, do: @fields
  defstruct @fields
end

defmodule Foo do
  require Record
  Record.defrecord :bar, Foo.Bar, Foo.Bar.fields
end

defmodule Foo.Utils do
  require Foo

  def record_to_struct(record) do
    [{:__struct__, Foo.Bar} | Foo.bar(record)] |> Enum.into(%{})
  end

  def struct_to_record(struct) do
    map = Map.from_struct(struct)
    for {key, default} <- Foo.Bar.fields, into: [Foo.Bar] do
      Dict.get(map, key, default)
    end |> List.to_tuple
  end
end

帕特里克的所有免责声明和信息都是正确的。如果不对字段进行注释,您将无法在运行时解决问题。

但是,如果您从 Erlang 记录转换(这通常是使用它们的唯一原因),则可以在编译时解决此问题。我们在 Elixir 源码上做,将 Erlang 的 #file_info{} 转换成 %File.Stat{}:

https://github.com/elixir-lang/elixir/blob/master/lib/elixir/lib/file/stat.ex