В чем разница между `raise« foo »` и `raise Exception.new (« foo »)`?

103

В чем разница - техническая, философская, концептуальная или иная - между

raise "foo"

и

raise Exception.new("foo")

?

Джон Башир
источник

Ответы:

121

Технически, первый вызывает RuntimeError с сообщением, установленным в "foo", а второй вызывает Exception с сообщением, установленным в "foo".

На практике существует значительная разница между тем, когда вы хотите использовать первое и когда вы хотите использовать второе.

Проще говоря, вы, вероятно, хотите, чтобы RuntimeErrorне был Exception. Спасательный блок без аргументов будет ловить RuntimeErrors, но НЕ ловит Exceptions. Поэтому, если вы поднимете Exceptionв своем коде, этот код его не поймает:

begin
rescue
end

Чтобы поймать, Exceptionвам нужно будет сделать следующее:

begin
rescue Exception
end

Это означает, что в каком-то смысле Exceptionошибка «хуже», чем ошибка RuntimeError, потому что вам нужно проделать больше работы, чтобы исправить ее.

То, что вы хотите, зависит от того, как ваш проект обрабатывает ошибки. Например, в наших демонах в основном цикле есть пустое спасение, которое перехватит их RuntimeErrors, сообщит о них и затем продолжит. Но в одном или двух случаях мы хотим, чтобы демон действительно умер из-за ошибки, и в этом случае мы вызываем Exception, который проходит через наш «нормальный код обработки ошибок» и удаляется.

И снова, если вы пишете код библиотеки, вы, вероятно, захотите a RuntimeError, а не a Exception, поскольку пользователи вашей библиотеки будут удивлены, если она вызовет ошибки, которые rescueне может уловить пустой блок, и им потребуется некоторое время, чтобы понять, почему.

Наконец, я должен сказать, что RuntimeErrorэто подкласс StandardErrorкласса, и фактическое правило заключается в том, что, хотя вы можете использовать raise любой тип объекта, пустое поле rescueпо умолчанию будет перехватывать только все, что наследуется от StandardError. Все остальное должно быть конкретным.

Дэниел Люкрафт
источник
2
очень информативно, спасибо. несколько вещей: [1] Этот последний пункт был самым освещая, и позвольте мне открыть для себя в IRB то , что вы не упоминаете: RuntimeError < StandardError < Exception[2] таким образом, что второй блок кода будет поймать как исключение и RuntimeError [3] интересно / странно, что «голый» подъем и спасение работают с этим конкретным исключением [4], возможно, практическое правило состоит в том, чтобы вызвать RuntimeError в клиентский код, но поднять и спасти свои собственные исключения внутри собственного кода?
John Bachir
1
[1, 2] Ага. [3] не уверен ... [4] Когда я пишу код на самом профессиональном уровне, я обычно создаю собственные типы ошибок, которые наследуются от StandardError. Это не должно быть сложнее, чем несколько строк вроде class MissingArgumentsError < StandardError; end.
Дэниел Люкрафт,
Очень информативно, но в каких ситуациях вы захотите создать исключение, а не ошибку времени выполнения, если ошибка времени выполнения предпочтительна для написания libraray?
Чихунг Ю
35

Из официальной документации:

raise   
raise( string )
raise( exception [, string [, array ] ] )

Без аргументов вызывает исключение $!или RuntimeErrorесли $!равно nil. С одним Stringаргументом он вызывает RuntimeErrorстроку как сообщение. В противном случае первым параметром должно быть имя Exceptionкласса (или объекта, который возвращает Exceptionисключение при отправке). Необязательный второй параметр устанавливает сообщение, связанное с исключением, а третий параметр представляет собой массив информации обратного вызова. Исключения улавливаются условием восстановления begin...endблоков.

raise "Failed to create socket"
raise ArgumentError, "No parameters", caller
эннуикиллер
источник