Иерархия исключений в Ruby
Ошибка vs исключение — в Ruby перехватывают объекты через begin/rescue/ensure; фатальные сбои VM в обычный rescue не попадают.
Общая теория стека — Ошибки, исключения и отказоустойчивость.
О чём эта статья
Исключения в Ruby — объекты; перехват через begin / rescue / ensure, повторный выброс — raise. Иерархия Exception / StandardError и типичные классы ошибок.
Связь: управление.
Интерактивное демо — часть сценариев на Python (
try/except); в Ruby —begin/rescue/ensure, но стек вызовов и раскрутка те же. Подробнее: ошибки и исключения.
Play ITЗагрузка интерактивного демо…
В Ruby перехватывают объекты-исключения через begin / rescue / ensure; ensure выполняется при любом исходе, как finally.
В общий rescue без типа попадают только наследники StandardError — не перехватывайте так SystemExit и SignalException.
Повторный сбой — raise.
Теория стека и крэшей — ошибки и исключения.
Корневой класс
Exception
Основные подклассы Exception
-
SystemExit— вызывается при завершении программы (exit,Kernel#exit). -
SignalException— получение сигнала ОС (например,SIGINTпри нажатии Ctrl+C).Interrupt— частный случайSignalExceptionдляSIGINT.
-
ScriptError— ошибки синтаксиса и загрузки:LoadErrorNotImplementedErrorSyntaxError
-
StandardError— основной родитель для большинства исключений, возникающих в пользовательском коде.ArgumentErrorUncaughtThrowError
EncodingErrorCompatibilityError
FiberErrorIOErrorEOFError
IndexErrorKeyErrorStopIteration
LocalJumpErrorNameErrorNoMethodError
RangeErrorFloatDomainError
RegexpErrorRuntimeErrorSecurityErrorSystemCallError— базовый класс для системных ошибок (аналогErrno::*).- Подклассы создаются динамически —
Errno::ENOENT,Errno::EACCES,Errno::EEXISTи т.д.
- Подклассы создаются динамически —
ThreadErrorTypeErrorZeroDivisionError
-
fatal— внутренний псевдокласс (на самом деле не используется напрямую); некоторые фатальные ошибки VM могут быть представлены какfatal, но они не перехватываются обычнымrescue.
Особенности
rescueбез аргументов перехватывает толькоStandardErrorи его подклассы.
Пример:
begin
# ...
rescue => e
# то же, что rescue StandardError => e
end
- Чтобы перехватить все исключения (включая
SystemExit,SignalException), нужно явно указатьException:
begin
# ...
rescue Exception => e
# крайне не рекомендуется в production
end
-
Системные ошибки (
Errno::*) генерируются автоматически при ошибках системных вызовов (например, открытие несуществующего файла →Errno::ENOENT). -
NoMethodError— одна из самых частых ошибок: вызов несуществующего метода. -
KeyError— выбрасывается при использованииHash#fetchс отсутствующим ключом без значения по умолчанию. -
StopIteration— используется для завершения итераторов (внутренне вEnumerator).
Как получить список программно
Ruby позволяет инспектировать иерархию классов:
# Все подклассы Exception
def subclasses_of(klass)
klass.subclasses + klass.subclasses.flat_map { |k| subclasses_of(k) }
end
puts subclasses_of(Exception).map(&:name).sort
Примечание: метод
subclassesдоступен только в режиме отладки или при подключении соответствующих утилит; в общем случае можно использоватьObjectSpace.each_object(Class).
Рекомендации
- Для пользовательских исключений создавайте подклассы
StandardError:
class MyCustomError < StandardError; end
- Не перехватывайте
Exceptionбез веской причины — это может помешать корректному завершению программы (например, игнорированиюCtrl+C).