Structs in Elixir

Defining structs is pretty simple in Elixir.

We create a struct within a module (like we do with functions), for example

defmodule Person do
  defstruct firstName: "", lastName: "", age: nil
end

As you can see, we’re explicitly settings the default values.

We’ve set age to nil, but we can also declare values with the default value of nil implicitly, but the fields that are to be implicitly set to nil must be at the start of the struct and we use [ ] to enclose the struct, for example

defstruct [:age, firstName: "", lastName: ""]

We can also mark fields/keys as required using the @enforce_keys attribute, for example

@enforce_keys [:firstName, :lastName]
defstruct [:age, :firstName, :lastName]

Now if we try to create and empty instance (i.e not setting firstName and lastName) we’ll get an error such as “(ArgumentError) the following keys must also be given when building struct Person: [:firstName, :lastName]”, but we’re jumping ahead of ourselves, let’s find out how we actually do create an instance of our struct first.

Structs take the name of the module, hence this struct is Person and we can create an instance of the struct using the following syntax, %{}, as shown below

def create() do
  %Person{ firstName: "Scooby", lastName: "Doo", age: 30 }
end

Now if you’ve already encountered maps, you’ll see that the struct syntax %{} is the same to the map syntax and that’s because structs are built on top of maps (but do not have all the capabilities of maps).

We can create an instance of a struct with it’s default values be simply using the following (assuming we’re not using @enforce_keys here)

defaultPerson = %Person{}

and this will simply bind to an instance with firstName and lastName of “” and age of nil.

We use standard “dot” syntax to, for example

iex(1)> scooby = Person.create()
%Person{firstName: "Scooby", lastName: "Doo", age: 30}
iex(2)> scooby.firstName
"Scooby"

We can create a new instance of a Person, where we change some fields using the | (update syntax), for example

iex(3)> scrappy = %{scooby | firstName: "Scrappy"}
%Person{firstName: "Scrappy", lastName: "Doo", age: 30}

We can also bind to a struct using pattern matching, i.e.

iex(4)> %Person{firstName: firstName} = scrappy
%Person{firstName: "Scrappy", lastName: "Doo", age: 30}
iex(5)> firstName
"Scrappy"

I said that structs were built on top of maps so let’s see if that’s true, try this

is_map(scooby)

and you’ll see true.

Structs can be made up of basic and more complicated types, i.e. structs can have fields which themselves are structs and so on.