Ruby имеет два разных механизма исключения: Throw/Catch and Raise/Rescue.
Почему у нас есть два?
Когда вы должны использовать один, а не другой?
Ruby имеет два разных механизма исключения: Throw/Catch and Raise/Rescue.
Почему у нас есть два?
Когда вы должны использовать один, а не другой?
Я думаю, http://hasno.info/ruby-gotchas-and-caveats имеет достойное объяснение разницы:
catch/throw - это не то же самое, что рейз/спасение. catch/throw позволяет быстро выходить из блоков обратно в точку, где улов определен для определенного символа, повышение спасения - это реальный материал обработки исключений, включающий объект Exception.
raise
, fail
, rescue
и ensure
обрабатывать ошибки, также известные как исключенияthrow
и catch
поток управленияВ отличие от других языки, Rubys throw и catch не используются для исключений. Вместо этого они предоставляют способ прекратить выполнение раньше, когда нет необходима дальнейшая работа. (Grimm, 2011)
Прекращение одного уровня потока управления, как и цикл while
, может быть выполнено с помощью простого return
. Завершение многих уровней управляющего потока, например вложенного цикла, может быть выполнено с помощью throw
.
В то время как механизм исключения для повышения и спасения отлично подходит для отказа от выполнения, когда что-то идет не так, иногда бывает приятно выпрыгнуть из какой-то глубоко вложенной конструкции во время нормальной обработки. Это - то, где поймать и бросить пригодится. (Thomas and Hunt, 2001)
https://coderwall.com/p/lhkkug/don-t-confuse-ruby-s-throw-statement-with-raise предлагает отличное объяснение, которое, я сомневаюсь, я могу улучшить. Подводя итог, нажимая некоторые образцы кода из сообщения в блоге, когда я иду:
raise
/rescue
являются ближайшими аналогами конструкции throw
/catch
, с которой вы знакомы с других языков (или с Python raise
/except
). Если вы столкнулись с условием ошибки, и вы бы throw
над ним на другом языке, вы должны raise
в Ruby.
Ruby throw
/catch
позволяет вам выполнить выполнение и поднять стек, ищущий catch
(например, raise
/rescue
), но на самом деле не предназначен для условий ошибки, Его следует использовать редко, и существует только тот момент, когда "подойти к стеку, пока вы не найдете соответствующее поведение catch
", имеет смысл для алгоритма, который вы пишете, но не имеет смысла думать о throw
как соответствующее условию ошибки.
Что такое catch и throw, используемый в Ruby? предлагает несколько предложений о хорошем использовании конструкции throw
/catch
.
Конкретные поведенческие различия между ними включают:
rescue Foo
приведет к спасению экземпляров Foo
, включая подклассы Foo
. catch(foo)
будет захватывать только один объект, Foo
. Вы не только не можете передать catch
имя класса, чтобы поймать его экземпляры, но даже не проверите сравнения равенств. Например,
catch("foo") do
throw "foo"
end
предоставит вам UncaughtThrowError: uncaught throw "foo"
(или ArgumentError
в версиях Ruby до 2.2)
Можно указать несколько предложений о спасении...
begin
do_something_error_prone
rescue AParticularKindOfError
# Insert heroism here.
rescue
write_to_error_log
raise
end
в то время как несколько catch
es должны быть вложенными...
catch :foo do
catch :bar do
do_something_that_can_throw_foo_or_bar
end
end
Голый rescue
эквивалентен rescue StandardError
и является идиоматической конструкцией. "Голый catch
", например catch() {throw :foo}
, никогда ничего не поймает и не должен использоваться.