Исходный код Структуры
Мы познакомились с картами в предыдущих главах:
iex> map = %{a: 1, b: 2}
%{a: 1, b: 2}
iex> map[:a]
1
iex> %{map | a: 3}
%{a: 3, b: 2}
Структуры — это расширения, построенные поверх карт, которые предоставляют проверки на этапе компиляции и значения по умолчанию.
Определение структур
Для определения структуры используется конструкция defstruct/1:
iex> defmodule User do ...> defstruct name: "John", age: 27 ...> end
Список ключевых слов, используемый с defstruct, определяет поля структуры вместе с их значениями по умолчанию. Структуры принимают имя модуля, в котором они определены. В примере выше мы определили структуру с именем User.
Теперь мы можем создавать User структуры, используя синтаксис, аналогичный синтаксису создания карт:
iex> %User{}
%User{age: 27, name: "John"}
iex> %User{name: "Jane"}
%User{age: 27, name: "Jane"}
Структуры обеспечивают гарантии на этапе компиляции, что только поля, определенные с помощью defstruct , будут разрешены в структуре:
iex> %User{oops: :field}
** (KeyError) key :oops not found expanding struct: User.__struct__/1
Доступ к структурам и их обновление
Структуры используют тот же синтаксис для доступа к полям и их обновления, что и карты с фиксированными ключами:
iex> john = %User{}
%User{age: 27, name: "John"}
iex> john.name
"John"
iex> jane = %{john | name: "Jane"}
%User{age: 27, name: "Jane"}
iex> %{jane | oops: :field}
** (KeyError) key :oops not found in: %User{age: 27, name: "Jane"}
При использовании синтаксиса обновления (|) Elixir знает, что новые ключи к структуре не будут добавлены, что позволяет картам внутри структуры совместно использовать их структуру в памяти. В примере выше, как john, так и jane используют ту же структуру ключей в памяти.
Структуры также могут использоваться в шаблонах соответствия, как для сопоставления значений определенных ключей, так и для обеспечения того, что сопоставляемое значение является структурой того же типа, что и сопоставляемое значение.
iex> %User{name: name} = john
%User{age: 27, name: "John"}
iex> name
"John"
iex> %User{} = %{}
** (MatchError) no match of right hand side value: %{}
Структуры — это обычные карты внутри
Структуры — это просто карты со специальным полем под названием __struct__ , которое содержит имя структуры:
iex> is_map(john) true iex> john.__struct__ User
Однако структуры не наследуют никаких протоколов карт. Например, вы не можете перечислить или получить доступ к структуре:
iex> john = %User{}
%User{age: 27, name: "John"}
iex> john[:name]
** (UndefinedFunctionError) function User.fetch/2 is undefined (User does not implement the Access behaviour)
User.fetch(%User{age: 27, name: "John"}, :name)
iex> Enum.each(john, fn {field, value} -> IO.puts(value) end)
** (Protocol.UndefinedError) protocol Enumerable not implemented for %User{age: 27, name: "John"} of type User (a struct)
Структуры и протоколы предоставляют одну из самых важных функций для разработчиков Elixir: полиморфизм данных. Это то, что мы рассмотрим в следующей главе.
Значения по умолчанию и обязательные ключи
Если при определении структуры вы не укажете значение по умолчанию для ключа, будет использоваться nil:
iex> defmodule Product do
...> defstruct [:name]
...> end
iex> %Product{}
%Product{name: nil}
Можно определить структуру, объединяющую поля со явными значениями по умолчанию и неявными nil значениями. В этом случае необходимо сначала указать поля, значения по умолчанию для которых неявно равны nil:
iex> defmodule User do
...> defstruct [:email, name: "John", age: 27]
...> end
iex> %User{}
%User{age: 27, email: nil, name: "John"}
Делая это в обратном порядке, будет выведено синтаксическое сообщение об ошибке:
iex> defmodule User do ...> defstruct [name: "John", age: 27, :email] ...> end ** (SyntaxError) iex:107: unexpected expression after keyword list. Keyword lists must always come last in lists and maps.
Вы также можете потребовать, чтобы определенные ключи были указаны при создании структуры, используя атрибут модуля @enforce_keys:
iex> defmodule Car do
...> @enforce_keys [:make]
...> defstruct [:model, :make]
...> end
iex> %Car{}
** (ArgumentError) the following keys must also be given when building struct Car: [:make]
expanding struct: Car.__struct__/1
Принудительное указание ключей обеспечивает простую гарантию на этапе компиляции для помощи разработчикам при создании структур. Она не применяется при обновлении и не обеспечивает никакой проверки значений.
© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.17.2/structs.html