Elevando errores con raise

Cuando encontramos una situación anómala en nuestras funciones, podemos interrumpir la evaluación del resto de la función mediante el comando `raise`, que nos permite lanzar hacia fuera un Error que puede ser tratado en otro bloque try-rescue.

En Elixir, además de capturar errores con la palabra clave rescue, también podemos generar nuestros propios errores usando raise. Esto resulta especialmente útil cuando estamos desarrollando librerías o interfaces más sofisticadas y queremos evitar el uso de pattern matching o tuplas para manejar estados incorrectos.

Para ilustrarlo, imaginemos que tenemos una función dividir que toma dos argumentos, x y y. Queremos evitar que se produzca un error aritmético al dividir por cero. Si detectamos que y es igual a cero, podemos detener la ejecución y lanzar un error con raise. Por ejemplo, podemos usar un error predefinido como ArgumentError y acompañarlo de un mensaje personalizado que explique el problema:

def dividir(x, y) do
  if y == 0 do
    raise ArgumentError, message: "No me puedes dividir entre 0, por favor"
  else
    x / y
  end
end

Si llamamos a dividir(5, 2), la función devolverá 2.5 sin problemas. Pero si intentamos dividir(5, 0), se lanzará un ArgumentError con el mensaje que hemos definido, deteniendo la ejecución y señalando el error.

Existe también una forma más sencilla de usar raise: pasarle directamente una cadena de texto. En ese caso, Elixir lanzará un RuntimeError, que es el error más genérico. Por ejemplo:

raise "Esto es un error genérico"

Aunque no es la forma más precisa, puede ser útil cuando queremos lanzar un error rápido sin definir un tipo específico.

Para crear errores personalizados, Elixir nos permite definir excepciones propias con def_exception. Esto se hace dentro de un módulo, donde podemos establecer un mensaje predeterminado para el error. Por ejemplo:

defmodule Calculadora.ErrorCalculo do
  defexception message: "Se ha producido un error de cálculo"
end

Luego, podemos lanzar este error personalizado con:

raise Calculadora.ErrorCalculo

De esta manera, el error ya incluye un mensaje por defecto, y no es necesario especificarlo cada vez que lo usamos.

Además, en Elixir existen funciones que terminan con una exclamación, como File.read!. Estas funciones lanzan un error automáticamente si algo falla, en lugar de devolver una tupla con el resultado y el error. Por ejemplo, File.read("archivo.txt") devuelve {:ok, contenido} o {:error, razón}, mientras que File.read!("archivo.txt") devuelve directamente el contenido o lanza un error si el archivo no existe.

Este comportamiento es útil cuando estamos seguros de que la operación debería funcionar y preferimos que el proceso se detenga en caso de error, sin necesidad de manejarlo explícitamente. Sin embargo, debemos tener en cuenta que si ocurre un error, la excepción se propagará y tendremos que capturarla o aceptar que el proceso falle.

Este patrón se repite en varias funciones de Elixir, como fetch!, pop! y otras, donde la versión con exclamación lanza errores en lugar de devolver valores que indiquen fallo.

Por último, aunque Elixir también tiene la construcción throw para lanzar valores y abortar la ejecución de una función, su uso es menos común y más especializado. Se puede capturar con catch, pero generalmente es recomendable evitarlo y preferir raise y rescue para el manejo de errores.

Así, en Elixir contamos con varias herramientas para generar y manejar errores de forma clara y estructurada, desde lanzar errores genéricos o personalizados con raise, hasta aprovechar funciones que lanzan excepciones automáticamente para simplificar el código cuando estamos seguros del éxito de una operación.

Lista de reproducción
  1. 1
    mix
    10 minutos
  2. 2
    Documentando código: comentarios, docs y moduledocs
    10 minutos
  3. 3
    Atributos de módulo
    9 minutos
  4. 4
    Dependencias
    12 minutos
  5. 5
    Un ejemplo práctico de módulo útil
    13 minutos
  6. 6
    Alias e import
    10 minutos
  7. 7
    Sobre las macros, require y use
    11 minutos
  8. 8
    Typespecs (parte 1, usando tipos básicos)
    10 minutos
  9. 9
    Typespecs (parte 2, tipos propios y t())
    11 minutos
  10. 10
    Comportamientos
    11 minutos
  11. 11
    Tratamiento de errores con rescue
    8 minutos
  12. 12
    Elevando errores con raise
    8 minutos
  13. 13
    with
    14 minutos
  14. 14
    Sigilos
    8 minutos
  15. 15
    Tests con ExUnit
    12 minutos
  16. 16
    Más particularidades de ExUnit
    13 minutos
  17. 17
    Microservicios en Elixir con Plug
    11 minutos
  18. 18
    Cómo Plug.Router te ayuda a escribir microservicios en Elixir
    14 minutos
  19. 19
    ¿Cómo hacer rutas dinámicas en Phoenix y Plug?
    13 minutos