https://coderwall.com/p/lhkkug/don-t-confuse-ruby-s-throw-statement-with-raise предлагает отличное объяснение, которое, я сомневаюсь, смогу улучшить. Подводя итог, прощупывая некоторые примеры кода из поста в блоге, я продолжаю:
raise
/ rescue
являются наиболее близкими аналогами конструкции throw
/ catch
, с которой вы знакомы по другим языкам (или по raise
/ except
в Python). Если вы столкнулись с ошибкой, и вам нужно throw
переписать ее на другом языке, вам следует raise
в Ruby.
Ruby's throw
/ catch
позволяет вам прервать выполнение и подняться по стеку в поисках catch
(как это делает raise
/ rescue
), но на самом деле не предназначен для условий ошибки , Его следует использовать редко, и он предназначен только для случаев, когда «подниматься по стеку, пока вы не найдете соответствующее поведение 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
должны быть вложены ...
catch :foo do
catch :bar do
do_something_that_can_throw_foo_or_bar
end
end
Голый rescue
эквивалентен rescue StandardError
и является идиоматической конструкцией. «Голый catch
», такой как catch() {throw :foo}
, никогда ничего не поймает и не должен использоваться.