Эликсир - обработка ошибок
В Elixir есть три механизма ошибок: ошибки, выбросы и выходы. Разберем каждый механизм подробно.
ошибка
Ошибки (или исключения) используются, когда в коде происходят исключительные вещи. Пример ошибки можно получить, пытаясь добавить число в строку -
IO.puts(1 + "Hello")
Когда вышеуказанная программа запускается, она выдает следующую ошибку -
** (ArithmeticError) bad argument in arithmetic expression
:erlang.+(1, "Hello")
Это была примерная встроенная ошибка.
Возникновение ошибок
Мы можем raiseошибки с использованием функций повышения. Давайте рассмотрим пример, чтобы понять то же самое -
#Runtime Error with just a message
raise "oops" # ** (RuntimeError) oops
Другие ошибки могут быть вызваны с помощью команды raise / 2, передающей имя ошибки и список аргументов ключевого слова.
#Other error type with a message
raise ArgumentError, message: "invalid argument foo"
Вы также можете определять свои собственные ошибки и поднимать их. Рассмотрим следующий пример -
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
Спасение ошибок
Мы не хотим, чтобы наши программы внезапно завершали работу, а, скорее, нужно внимательно обрабатывать ошибки. Для этого мы используем обработку ошибок. Мыrescue ошибки с использованием try/rescueпостроить. Давайте рассмотрим следующий пример, чтобы понять то же самое -
err = try do
raise "oops"
rescue
e in RuntimeError -> e
end
IO.puts(err.message)
Когда вышеуказанная программа запускается, она дает следующий результат -
oops
Мы обработали ошибки в операторе восстановления, используя сопоставление с образцом. Если мы не используем ошибку и просто хотим использовать ее для целей идентификации, мы также можем использовать форму -
err = try do
1 + "Hello"
rescue
RuntimeError -> "You've got a runtime error!"
ArithmeticError -> "You've got a Argument error!"
end
IO.puts(err)
При запуске вышеуказанной программы он дает следующий результат -
You've got a Argument error!
NOTE- Большинство функций в стандартной библиотеке Elixir реализованы дважды, один раз возвращая кортежи, а другой - ошибки. Например,File.read и File.read!функции. Первый возвратил кортеж, если файл был успешно прочитан, и если произошла ошибка, этот кортеж использовался для указания причины ошибки. Второй вызывал ошибку, если была обнаружена ошибка.
Если мы используем подход с использованием первой функции, то нам нужно использовать прецедент для сопоставления с шаблоном ошибки и предпринимать соответствующие действия. Во втором случае мы используем подход try rescue для кода, подверженного ошибкам, и обрабатываем ошибки соответственно.
Броски
В Elixir значение может быть выброшено, а затем обнаружено. Throw и Catch зарезервированы для ситуаций, когда невозможно получить значение, кроме случаев использования throw и catch.
На практике экземпляры встречаются довольно редко, за исключением случаев взаимодействия с библиотеками. Например, давайте теперь предположим, что модуль Enum не предоставляет никакого API для поиска значения и что нам нужно найти первое кратное 13 в списке чисел -
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)
Когда вышеуказанная программа запускается, она дает следующий результат -
Got 26
Выход
Когда процесс умирает по «естественным причинам» (например, из-за необработанных исключений), он отправляет сигнал выхода. Процесс также может умереть, явно посылая сигнал выхода. Давайте рассмотрим следующий пример -
spawn_link fn -> exit(1) end
В приведенном выше примере связанный процесс завершился, послав сигнал выхода со значением 1. Обратите внимание, что exit также можно «поймать» с помощью try / catch. Например -
val = try do
exit "I am exiting"
catch
:exit, _ -> "not really"
end
IO.puts(val)
Когда вышеуказанная программа запускается, она дает следующий результат -
not really
После
Иногда необходимо обеспечить очистку ресурса после некоторого действия, которое потенциально может вызвать ошибку. Конструкция try / after позволяет вам это делать. Например, мы можем открыть файл и использовать предложение after, чтобы закрыть его, даже если что-то пойдет не так.
{:ok, file} = File.open "sample", [:utf8, :write]
try do
IO.write file, "olá"
raise "oops, something went wrong"
after
File.close(file)
end
Когда мы запустим эту программу, она выдаст нам ошибку. Ноafter Заявление гарантирует, что дескриптор файла будет закрыт при любом таком событии.