The idea is that the match will pass if the user variable is a %User{} struct and raise an error otherwise. When it’s in a function head like that it’s used to control whether or not the user variable can enter that function. If it’s not a %User{} struct then it won’t be able to go in there. It will look for other versions of change_registration that produce a successful pattern match. If there are none an error will raise.
A =inside a pattern which is what you are have in your function head just measn that both sides must match. So writing %User{} = user in a pattern means that both the struct %User and the variable user must match. This means that writing user = %User{} in a pattern has the same meaning, both sides must match.
However, and this is a big however, using = in the body of a function has a different meaning. It is a match operator, pattern = expression which first evaluates the expression in RHS and then matches that with the pattern in the LHS. It goes right-to-left.
It was perhaps not so smart to use = in these different ways, but it was inherited fro Erlang