jonnifyelixir
F3 / F3.05 / define

F3.05 · part 1 of 3

Defining a struct

A struct is declared inside a module with defstruct, which lists the fields the struct may hold. The module's name becomes the struct's name, so Portal.Accounts.User defines the %User{} shape. At runtime a struct is an ordinary map carrying one extra key, __struct__, set to the module.

The portal needs a learner with a guaranteed shape: an id, an email, and a role. One defstruct line gives every %User{} exactly those fields and nothing else.

A struct is a tagged map

The %User{} literal is sugar over a map. It shows the fields you set, but the same value also carries a hidden __struct__ key naming its module. Switch between the views to see the one value three ways.

One value, three views · select one

FIELDS HELD BY THE VALUE __struct__: Portal.Accounts.User id: "USR0NbAb1xcFCy" email: "ada@portal.dev" role: :student

      

The literal and the map are the same value. The __struct__ key is the only thing that separates a struct from a plain map — and it is what lets a later clause match on the type.

Declaring the fields

The declaration is one line. defstruct takes a list of field names; building a value with an unknown key is a compile error, which is the guarantee a plain map cannot give.

defmodule Portal.Accounts.User do
  defstruct [:id, :email, :role]
end

# build one — and look underneath
user = %Portal.Accounts.User{id: "USR0NbAb1xcFCy", email: "ada@portal.dev", role: :student}

user.__struct__   # => Portal.Accounts.User
Map.keys(user)   # => [:__struct__, :email, :id, :role]
is_map(user)      # => true

F2.07 · product types

A product type bundles a fixed set of fields into one value — all present together.

F3 · defstruct

The same product, named by its module and backed by a map — the fields are fixed, the shape is checked.

Next: enforcing keys and defaults — making some fields required and giving others a starting value.

References

Primary sources for this lesson, and where it connects in the course.