The right way to update Elixir structs (and how not to do it 😁)
Notes
I think there is a right way (and a wrong way) to update Elixir structs – at least that’s if you want to know when you accidentally type the wrong thing!
Since structs are maps, we might be tempted to use Map.put/3
.
iex> user = %User{name: "Boromir", admin: true}
iex> user |> Map.put(:admin, false)
# => %User{name: "Boromir", admin: false}
See, it works!…
… until you make a typo.
iex> user = %User{name: "Boromir", admin: true}
iex> user |> Map.put(:amdin, false) # <- typo!!
# => %{name: "Boromir", __struct__: User, admin: true, amdin: false}
😱 that’s not what we meant to do, is it?
Compare that to the special syntax to update structs:
iex> user = %User{name: "Boromir", admin: true}
iex> %{user | admin: false }
%User{name: "Boromir", admin: false}
iex> %{user | amdin: false} # <- typo!!
** (KeyError) key :amdin not found in: %User{name: "Boromir", admin: true}. Did you mean:
* :admin
🤩 look at that beautiful message! Elixir’s got our back!
But I already know what you’ll be saying…
I hear you. I hear you.
Thankfully, the Elixir team heard that too, and they introduced Map.replace/3
and Map.replace!/3
.
Check it out!
iex> user |> Map.replace(:amdin, false)
%User{name: "Boromir", admin: true}
As you can see, Map.replace/3
doesn’t put a non-existent value into the map.
So the typo doesn’t just magically add keys to your map.
But… we now have a silent error. Maybe we want to be loud!
iex> user |> Map.replace!(:amdin, false)
** (KeyError) key :amdin not found in: %User{name: "Boromir", admin: true}. Did you mean:
* :admin
There you go! Two great ways to update your structs – safety included.