What Is a Struct?
A struct is declared with defstruct inside a defmodule block and defines a fixed, named set of fields, unlike a plain map, which accepts any arbitrary key. Internally, a struct is just a map carrying a special __struct__ key whose value is the defining module's name, which is how Elixir tags a value as belonging to that specific struct type.
Cricket analogy: A struct is like an official ICC scorecard template with fixed fields -- batter, runs, balls faced, fours, sixes -- that every entry must follow, unlike a scribbled press-box note where anyone could jot down any random field like 'lucky charm' without structure.
Defining and Creating Structs
Inside a module, defstruct name: nil, age: 0, active: true declares the struct's fields along with default values used whenever a field is omitted at creation time; %User{name: "Ada", age: 30} builds one, leaving active at its default of true. Referencing a field that wasn't declared in defstruct -- whether creating or updating -- is caught as a compile-time error, which catches typos that a plain map would silently accept as a brand-new key.
Cricket analogy: %Player{name: "Kohli", role: :batter} is like filling an official BCCI registration form -- leave batting_style blank and it defaults per the template, but write an unrecognized field like lucky_charm and the registration is rejected outright before the season starts.
defmodule User do
@enforce_keys [:name]
defstruct name: nil, age: 0, active: true
end
user = %User{name: "Ada", age: 30}
older_user = %{user | age: 31}
%User{name: name} = older_user
name # "Ada"
defimpl String.Chars, for: User do
def to_string(u), do: "#{u.name} (#{u.age})"
end
IO.puts(older_user) # "Ada (31)"
# %User{naem: "typo"} raises a compile-time error:
# ** (KeyError) key :naem not found in: %User{...}
defstruct alone lets every field default to nil (or whatever you specify) if the caller omits it, which can hide a genuinely required field as a silent nil. Add @enforce_keys [:name] above defstruct to make Elixir raise ArgumentError at struct-creation time if :name isn't supplied, catching missing required data immediately instead of letting a nil propagate silently.
Updating and Pattern Matching Structs
The update syntax %{struct | field: value} returns a new struct with the given field(s) changed and every other field preserved, without needing to restate the whole struct. Pattern matching with %User{name: n} = value checks two things at once: that value is a map shaped like a User, and that its __struct__ tag specifically equals User -- so it won't match an %Admin{} struct even if Admin happens to also have a name field.
Cricket analogy: %{player | runs: 50} is like updating just the runs field on an existing scorecard without retyping the whole card, and matching %Batter{name: n} = entry only succeeds if the card is truly tagged as a Batter record, not a Bowler record sharing a similarly shaped map.
Structs and Protocols
Structs don't automatically gain polymorphic behavior just because they're maps -- functions dispatched through a protocol, like Enum.map/2 (via the Enumerable protocol) or to_string/1 (via String.Chars), require an explicit defimpl SomeProtocol, for: YourStruct before they'll work on a custom struct. This keeps a struct's behavior opt-in and explicit rather than silently inherited from whatever protocol implementations happen to exist for maps in general.
Cricket analogy: A struct without a protocol implementation is like a new overseas signing who hasn't learned the team's fielding signals yet -- you must explicitly coach them (defimpl) before they join the squad's standard communication, just as a struct needs Enumerable before Enum functions work on it.
Because a struct is fundamentally a map with a __struct__ key, ordinary Map functions like Map.get/2, Map.keys/1, and Map.fetch/2 work on a struct directly without any protocol implementation -- it's specifically the protocol-dispatched functions like those in Enum and String.Chars that require an explicit defimpl.
- defstruct inside a module defines a struct's fixed set of fields and their default values.
- A struct is internally a map with a special __struct__ key identifying which module defined it.
- Creating or updating a struct with an unrecognized field name is a compile-time error, unlike plain maps.
- @enforce_keys makes Elixir raise ArgumentError at creation time if a required field is missing.
- The update syntax %{struct | field: value} changes specific fields without retyping the whole struct.
- Pattern matching %ModuleName{} = value only succeeds if value is tagged as that exact struct.
- Structs need an explicit defimpl to support protocol-dispatched behavior like Enum functions or to_string, though plain Map functions work on them directly.
Practice what you learned
1. What does `defstruct name: nil, age: 0` do inside a module?
2. What happens if you write `%User{naem: "Ada"}` with a typo'd field name?
3. What does @enforce_keys [:name] do above a defstruct?
4. Why does `%User{name: n} = value` fail to match if value is an `%Admin{name: "Sam"}` struct with the same field names?
5. Why doesn't Enum.map work automatically on a custom struct?
Was this page helpful?
You May Also Like
Functions and Modules in Elixir
Learn how Elixir organizes code into modules and functions, covering named functions, arity, default arguments, private functions, module attributes, and anonymous functions.
Guards and case in Elixir
Learn how Elixir's case expression and guard clauses let you branch on pattern matches with additional boolean conditions, and when to reach for cond instead.
Recursion and Enum
Explore how Elixir uses recursion for iteration and how the Enum and Stream modules provide higher-level functions built on that foundation.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics