Custom Errors in Elixir

Our first example demonstrates how to create custom errors in Elixir. This is similar to implementing the Error() method in other languages, but Elixir uses a different approach with its protocol system.

defmodule CustomErrors do
  # Define a custom error struct
  defmodule ArgError do
    @enforce_keys [:arg, :message]
    defstruct [:arg, :message]

    # Implement the Exception protocol for our custom error
    defimpl String.Chars, for: ArgError do
      def to_string(error), do: "#{error.arg} - #{error.message}"
    end
  end

  def f(arg) do
    if arg == 42 do
      # Return our custom error
      {:error, %ArgError{arg: arg, message: "can't work with it"}}
    else
      {:ok, arg + 3}
    end
  end

  def main do
    case f(42) do
      {:error, %ArgError{} = error} ->
        IO.puts("#{error.arg}")
        IO.puts(error.message)
      _ ->
        IO.puts("err doesn't match ArgError")
    end
  end
end

CustomErrors.main()

In this example, we define a custom error struct ArgError with arg and message fields. We implement the String.Chars protocol for our custom error, which is similar to implementing the Error() method in other languages.

The f function demonstrates how to return our custom error when a specific condition is met (in this case, when the argument is 42).

In the main function, we use pattern matching to check if the returned error matches our ArgError struct. This is similar to using errors.As in other languages, allowing us to extract and use the fields of our custom error.

To run the program:

$ elixir custom_errors.exs
42
can't work with it

This example shows how Elixir’s pattern matching and protocol system can be used to create and handle custom errors effectively.