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.