Elixir - Manejo de errores
Elixir tiene tres mecanismos de error: errores, lanzamientos y salidas. Exploremos cada mecanismo en detalle.
Error
Los errores (o excepciones) se utilizan cuando suceden cosas excepcionales en el código. Se puede recuperar un error de muestra al intentar agregar un número en una cadena:
IO.puts(1 + "Hello")
Cuando se ejecuta el programa anterior, produce el siguiente error:
** (ArithmeticError) bad argument in arithmetic expression
:erlang.+(1, "Hello")
Este fue un error incorporado de muestra.
Generando errores
Podemos raiseerrores al usar las funciones de aumento. Consideremos un ejemplo para entender lo mismo:
#Runtime Error with just a message
raise "oops" # ** (RuntimeError) oops
Se pueden generar otros errores con raise / 2 pasando el nombre del error y una lista de argumentos de palabras clave
#Other error type with a message
raise ArgumentError, message: "invalid argument foo"
También puede definir sus propios errores y plantearlos. Considere el siguiente ejemplo:
defmodule MyError do
defexception message: "default message"
end
raise MyError # Raises error with default message
raise MyError, message: "custom message" # Raises error with custom message
Rescatar errores
No queremos que nuestros programas se cierren abruptamente, sino que los errores deben manejarse con cuidado. Para ello utilizamos el manejo de errores. Nosotrosrescue errores usando el try/rescueconstruir. Consideremos el siguiente ejemplo para entender lo mismo:
err = try do
raise "oops"
rescue
e in RuntimeError -> e
end
IO.puts(err.message)
Cuando se ejecuta el programa anterior, produce el siguiente resultado:
oops
Hemos manejado errores en la declaración de rescate usando la coincidencia de patrones. Si no tenemos ningún uso del error, y solo queremos usarlo con fines de identificación, también podemos usar el formulario -
err = try do
1 + "Hello"
rescue
RuntimeError -> "You've got a runtime error!"
ArithmeticError -> "You've got a Argument error!"
end
IO.puts(err)
Cuando se ejecuta el programa anterior, produce el siguiente resultado:
You've got a Argument error!
NOTE- La mayoría de las funciones de la biblioteca estándar de Elixir se implementan dos veces, una devuelve tuplas y la otra genera errores. Por ejemplo, elFile.read y el File.read!funciones. El primero devolvió una tupla si el archivo se leyó correctamente y si se encontró un error, esta tupla se usó para dar la razón del error. El segundo generó un error si se encontró un error.
Si usamos el enfoque de la primera función, entonces necesitamos usar el caso para el patrón que coincide con el error y tomar medidas de acuerdo con eso. En el segundo caso, utilizamos el enfoque de prueba de rescate para el código propenso a errores y manejamos los errores en consecuencia.
Lanza
En Elixir, se puede lanzar un valor y luego ser capturado. Lanzar y atrapar están reservados para situaciones en las que no es posible recuperar un valor a menos que se utilice lanzar y atrapar.
Las instancias son bastante poco comunes en la práctica, excepto cuando interactúan con bibliotecas. Por ejemplo, supongamos ahora que el módulo Enum no proporcionó ninguna API para encontrar un valor y que necesitábamos encontrar el primer múltiplo de 13 en una lista de números:
val = try do
Enum.each 20..100, fn(x) ->
if rem(x, 13) == 0, do: throw(x)
end
"Got nothing"
catch
x -> "Got #{x}"
end
IO.puts(val)
Cuando se ejecuta el programa anterior, produce el siguiente resultado:
Got 26
Salida
Cuando un proceso muere por "causas naturales" (por ejemplo, excepciones no controladas), envía una señal de salida. Un proceso también puede morir al enviar explícitamente una señal de salida. Consideremos el siguiente ejemplo:
spawn_link fn -> exit(1) end
En el ejemplo anterior, el proceso vinculado murió al enviar una señal de salida con un valor de 1. Tenga en cuenta que la salida también se puede "capturar" usando try / catch. Por ejemplo
val = try do
exit "I am exiting"
catch
:exit, _ -> "not really"
end
IO.puts(val)
Cuando se ejecuta el programa anterior, produce el siguiente resultado:
not really
Después
A veces es necesario asegurarse de que un recurso se limpia después de alguna acción que potencialmente puede generar un error. La construcción try / after le permite hacer eso. Por ejemplo, podemos abrir un archivo y usar una cláusula after para cerrarlo, incluso si algo sale mal.
{:ok, file} = File.open "sample", [:utf8, :write]
try do
IO.write file, "olá"
raise "oops, something went wrong"
after
File.close(file)
end
Cuando ejecutamos este programa, nos dará un error. Pero elafter La declaración asegurará que el descriptor de archivo se cierre ante tal evento.