Kernel.defstruct

You're seeing just the macro defstruct, go back to Kernel module for more information.
Link to this macro

defstruct(fields)

View Source (macro)

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
end

Struct 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
end

The 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]
end

Deriving

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
end

Now 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}
end

It 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.