任意で渡ってきた変数が特定のStructかどうか判定する方法
is_struct
みたいな便利メソッドがないようなので以下のように判定する必要があった。
割と面倒だったのでまとめておく。
例として、任意で受け取った引数 args
が User
のStructかどうかを判定する。
StructはMapの拡張になっている
まずStructは Mapを拡張した拡張であるので、 is_map(args)
がtrueになる。
Structs are extensions on top of maps that bring default values, compile-time guarantees and polymorphism into Elixir.
ref: Structs - Elixir
iex(1)> user_struct = %User{} %User{...} iex(2)> string = "test" "test" iex(3)> is_map(user_struct) true iex(4)> is_map(string) false
Structは __struct__
というkeyを保持している
In the example above, pattern matching works because underneath structs are bare maps with a fixed set of fields. As maps, structs store a “special” field named struct that holds the name of the struct:
次に、Structの場合は __struct__
というkeyを保持しているので、「MapであるがStructでない変数」の場合は Map.has_key?(args, :__struct)
で弾くことが出来る。
iex(5)> map = %{hoge: "huga"} %{hoge: "huga"} iex(6)> map |> Map.has_key?(:__struct__) false iex(7)> user_struct |> Map.has_key?(:__struct__) true
__struct__
にはStructの名前が入っている
args.__struct__
が User
であればtrueになるので、あとはこれを比較すれば良い。
iex(8)> not_user_struct = %NotUser{} %NotUser{...} iex(9)> user_struct.__struct__ == User true iex(10)> not_user_struct.__struct__ == User false
まとめると
def is_user?(args) do is_map(args) && Map.has_key?(args, :__struct__) && args.__struct__ == User end