Pattern Matching

Pattern matching is one of Elixir’s defining features. In most languages = means "assign this value to this variable". Elixir reads = differently and calls it the match operator: "try to match the left-hand side against the right-hand side".

Here is the smallest possible example:

iex> x = 5 (1)
5
iex> 5 = x (2)
5
iex> 6 = x (3)
** (MatchError) no match of right hand side value: 5
1 A variable on the left is unbound, so Elixir binds x to 5.
2 Both sides have the same value, the match succeeds.
3 6 cannot match 5, so Elixir raises a MatchError.

Once you can describe a shape, you can destructure any value into parts. Here is a tuple example:

iex> {a, b, c} = {:hello, "world", 42}
{:hello, "world", 42}
iex> a
:hello
iex> b
"world"
iex> c
42

In that snippet, Elixir matches the variables a, b, and c against the three elements of the tuple on the right. If the shapes do not line up, you get a MatchError:

iex> {d, e} = {:hi, "there", 23}
** (MatchError) no match of right hand side value: {:hi, "there", 23}
Pattern matching is used in many more places than just =. It is how case picks a branch, how with chains a happy path, and how a function with several clauses decides which one runs. See the guards and multi-clause functions chapters for those applications.
Agentic Coding Tip: = is Pattern Matching, Not Assignment

Every other mainstream language reads = as "store the right-hand side in the left-hand side variable." Elixir reads it as "try to match the shape on the left against the value on the right, and bind any variables along the way." An agent that internalised the first reading will write code that looks right and raises MatchError at runtime.

The common shape of the trap:

# Agent writes:
{:ok, user} = Accounts.find_user(id)

# ...then Accounts.find_user/1 returns {:error, :not_found}
# for an unknown id, and the whole process crashes with
# ** (MatchError) no match of right hand side value:
#    {:error, :not_found}

This is sometimes exactly what you want — let it crash on the unexpected case, supervisors handle it. But the agent rarely reasons about that trade-off; it just picks the shortest shape. The safer default in most situations is case (or with), which lets you handle both branches explicitly:

case Accounts.find_user(id) do
  {:ok, user}      -> render_profile(user)
  {:error, reason} -> render_not_found(reason)
end

Rule to add to your project’s CLAUDE.md:

When calling a function that returns a tagged tuple
(`{:ok, _}` / `{:error, _}`) or any other value with
more than one possible shape, do NOT use `=` to match it
unless crashing on the unexpected shape is genuinely
what's wanted. Default to `case`, `with`, or a function
head with multiple clauses. If you do use `=`, add a
one-line comment explaining why letting the process
crash on a non-match is correct here.

Destructuring Lists

Elixir offers a special syntax to match the head and tail of a list:

iex> shopping_list = ["apple", "orange", "banana", "pineapple"] (1)
["apple", "orange", "banana", "pineapple"]
iex> [head | tail] = shopping_list (2)
["apple", "orange", "banana", "pineapple"]
iex> head
"apple"
iex> tail
["orange", "banana", "pineapple"]
iex> [first, second | rest] = shopping_list (3)
["apple", "orange", "banana", "pineapple"]
iex> first
"apple"
iex> second
"orange"
iex> rest
["banana", "pineapple"]
1 We bind a list to shopping_list.
2 [head | tail] is the special syntax that splits a list into its first element and the rest.
3 You can also peel off more than one element before the |.

If you know the exact length, you can match the list as a whole:

iex> [a, b, c, d] = ["apple", "orange", "banana", "pineapple"]
["apple", "orange", "banana", "pineapple"]
iex> a
"apple"
iex> [e, f, g] = ["apple", "orange", "banana", "pineapple"] (1)
** (MatchError) no match of right hand side value: ...
1 A three-element pattern cannot match a four-element list.

Destructuring Maps

Matching a map works a little differently. You only need to list the keys you care about:

iex> product_prices = %{apple: 0.5, orange: 0.7, pineapple: 1}
%{apple: 0.5, orange: 0.7, pineapple: 1}
iex> %{orange: price} = product_prices (1)
%{apple: 0.5, orange: 0.7, pineapple: 1}
iex> price
0.7
iex> %{orange: price1, apple: price2} = product_prices (2)
%{apple: 0.5, orange: 0.7, pineapple: 1}
iex> price1
0.7
iex> price2
0.5
1 Here we match just one key.
2 You can match several keys at once. The map may have more keys, they are simply ignored.

Destructuring Strings

Pattern matching also works on strings when the prefix is fixed:

iex> user = "Stefan Wintermeyer"
"Stefan Wintermeyer"
iex> "Stefan " <> last_name = user
"Stefan Wintermeyer"
iex> last_name
"Wintermeyer"
The left side of a <> operator in a match must always be a fixed string. Otherwise Elixir cannot tell where the split is.

Wildcards

Sometimes you want to match something but you do not care about the value. Use the _ wildcard, either on its own or as a prefix to a variable name, to tell Elixir you do not need the binding:

iex> cart = {"apple", "orange", "banana"}
{"apple", "orange", "banana"}
iex> {first, _, _} = cart (1)
{"apple", "orange", "banana"}
iex> first
"apple"

iex> cart2 = ["apple", "orange", "banana", "pineapple"]
["apple", "orange", "banana", "pineapple"]
iex> [head | _tail] = cart2 (2)
["apple", "orange", "banana", "pineapple"]
iex> head
"apple"
1 _ ignores the second and third elements of the tuple.
2 tail is still a wildcard, but the name describes the intent. Elixir will not warn about an unused variable when the name starts with .

The Pin Operator ^

By default, a bare variable on the left side of = is either bound (if unbound) or rebound (if already bound). Sometimes you want the opposite: "use the current value of this variable as part of the pattern, and fail if the right-hand side does not match it". That is what the pin operator ^ is for.

iex> expected = :ok
:ok
iex> ^expected = :ok (1)
:ok
iex> ^expected = :error (2)
** (MatchError) no match of right hand side value: :error
1 The pin operator says "match against the value :ok that expected is already bound to". The match succeeds.
2 :error is not equal to the pinned value, so the match fails.

Pinning is especially useful inside more complex patterns:

iex> key = :name
:name
iex> %{^key => name} = %{name: "Alice", age: 42} (1)
%{name: "Alice", age: 42}
iex> name
"Alice"
1 Without the pin, key on the left would be a new variable. With the pin, it is the atom :name taken from the already-bound variable.

Rebinding

A variable in Elixir is immutable: once you bind x = 5, the value 5 itself never changes. But the name x can be rebound to point at a different value:

iex> x = 5
5
iex> y = x (1)
5
iex> x = 10 (2)
10
iex> y (3)
5
1 y is bound to the current value of x, which is 5.
2 x is rebound to 10. The old value 5 is untouched.
3 y still holds 5. Rebinding x did not change y.
If you want to guarantee that a value cannot be accidentally rebound later in the same scope, use the pin operator on the next match: ^x = some_value.