Kernel.defstruct
defstruct, go back to Kernel module for more information.
Defines a struct.
A struct is a tagged map that allows developers to provide default values for keys, tags to be used in polymorphic dispatches and compile time assertions.
To define a struct, a developer must define both __struct__/0 and
__struct__/1 functions. defstruct/1 is a convenience macro which
defines such functions with some conveniences.
For more information about structs, please check Kernel.SpecialForms.%/2.
Examples
defmodule User do
defstruct name: nil, age: nil
endStruct fields are evaluated at compile-time, which allows
them to be dynamic. In the example below, 10 + 11 is
evaluated at compile-time and the age field is stored
with value 21:
defmodule User do
defstruct name: nil, age: 10 + 11
endThe fields argument is usually a keyword list with field names
as atom keys and default values as corresponding values. defstruct/1
also supports a list of atoms as its argument: in that case, the atoms
in the list will be used as the struct's field names and they will all
default to nil.
defmodule Post do
defstruct [:title, :content, :author]
endDeriving
Although structs are maps, by default structs do not implement
any of the protocols implemented for maps. For example, attempting
to use a protocol with the User struct leads to an error:
john = %User{name: "John"}
MyProtocol.call(john)
** (Protocol.UndefinedError) protocol MyProtocol not implemented for %User{...}defstruct/1, however, allows protocol implementations to be
derived. This can be done by defining a @derive attribute as a
list before invoking defstruct/1:
defmodule User do
@derive [MyProtocol]
defstruct name: nil, age: 10 + 11
end
MyProtocol.call(john) # it works!For each protocol in the @derive list, Elixir will assert the protocol has
been implemented for Any. If the Any implementation defines a
__deriving__/3 callback, the callback will be invoked and it should define
the implementation module. Otherwise an implementation that simply points to
the Any implementation is automatically derived. For more information on
the __deriving__/3 callback, see Protocol.derive/3.
Enforcing keys
When building a struct, Elixir will automatically guarantee all keys belongs to the struct:
%User{name: "john", unknown: :key}
** (KeyError) key :unknown not found in: %User{age: 21, name: nil}Elixir also allows developers to enforce certain keys must always be given when building the struct:
defmodule User do
@enforce_keys [:name]
defstruct name: nil, age: 10 + 11
endNow trying to build a struct without the name key will fail:
%User{age: 21}
** (ArgumentError) the following keys must also be given when building struct User: [:name]Keep in mind @enforce_keys is a simple compile-time guarantee
to aid developers when building structs. It is not enforced on
updates and it does not provide any sort of value-validation.
Types
It is recommended to define types for structs. By convention such type
is called t. To define a struct inside a type, the struct literal syntax
is used:
defmodule User do
defstruct name: "John", age: 25
@type t :: %__MODULE__{name: String.t(), age: non_neg_integer}
endIt is recommended to only use the struct syntax when defining the struct's
type. When referring to another struct it's better to use User.t instead of
%User{}.
The types of the struct fields that are not included in %User{} default to
term() (see term/0).
Structs whose internal structure is private to the local module (pattern
matching them or directly accessing their fields should not be allowed) should
use the @opaque attribute. Structs whose internal structure is public should
use @type.