Tratamiento de errores con rescue

Otra forma más de tratar errores es usar un bloque try-rescue para envolver el código problemático y capturar posibles errores que puedan ocurrir durante su uso para evaluar a expresiones alternativas en caso de error.

El tratamiento de errores en Elixir es un tema recurrente y fundamental para escribir código robusto. Aunque ya conocemos la forma habitual de manejar errores mediante funciones que devuelven tuplas con {:ok, resultado} o {:error, motivo}, no es la única manera. A veces, el código puede lanzar errores directamente, como cuando intentamos dividir un número entre cero, lo que genera un ArithmeticError. Estos errores vienen acompañados de mensajes descriptivos que nos ayudan a entender qué ha fallado y dónde.

En Elixir, la filosofía general es dejar que el sistema falle y se recupere reiniciando procesos, especialmente en entornos basados en OTP. Sin embargo, no siempre es necesario o conveniente reiniciar todo por un error trivial. En ocasiones, queremos capturar esos errores para manejarlos de forma más suave, ocultarlos o devolver valores alternativos que eviten que el fallo se propague.

Para esto, Elixir nos ofrece la construcción try-rescue. El bloque try intenta ejecutar un fragmento de código que puede ser problemático, y si ocurre un error, el bloque rescue nos permite capturarlo y decidir qué hacer con él. Esto nos da un control fino sobre los errores que queremos manejar y cómo queremos responder a ellos.

Veamos un ejemplo sencillo. Supongamos que queremos dividir 5 entre 0, lo cual sabemos que genera un error aritmético. Podemos envolver esta operación en un bloque try y usar rescue para capturar el error y devolver un valor alternativo, como :infinito:

result =
  try do
    x = 5
    y = 0
    x / y
  rescue
    ArithmeticError -> :infinito
  end

IO.inspect(result)

En este código, si la división falla con un ArithmeticError, en lugar de que el programa se detenga, el bloque rescue intercepta el error y devuelve :infinito. Si no ocurre ningún error, el resultado normal de la división se asigna a result.

Además, podemos manejar varios tipos de errores dentro del mismo bloque rescue usando pattern matching, similar a cómo funciona un case. Por ejemplo, podríamos capturar un error de protocolo indefinido (UndefinedFunctionError) o incluso un caso genérico para atrapar cualquier otro error:

result =
  try do
    # Código que puede fallar
  rescue
    ArithmeticError -> :infinito
    UndefinedFunctionError -> :funcion_no_definida
    _error -> :error_desconocido
  end

Otra característica útil es poder enlazar el error capturado a una variable para acceder a sus detalles, como el mensaje de error. Esto se hace usando la sintaxis variable in ErrorType. Por ejemplo:

result =
  try do
    5 / 0
  rescue
    e in ArithmeticError -> "Error aritmético: #{e.message}"
  end

IO.puts(result)

Aquí, e es la variable que contiene la estructura del error, y podemos acceder a su mensaje con e.message. Esto nos permite construir respuestas más informativas o realizar acciones específicas según el contenido del error.

En definitiva, el uso de try-rescue en Elixir nos permite capturar errores que de otro modo detendrían la ejecución, manejar diferentes tipos de errores con pattern matching, y devolver valores alternativos o mensajes personalizados. Así, podemos escribir código más resiliente y controlado, evitando que fallos menores afecten la estabilidad general de nuestra aplicació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