В чем разница между повышением исключений и созданием исключений в Ruby?

168

В Ruby есть два разных механизма исключений: Throw / Catch и Raise / Rescue.

Почему у нас есть два?

Когда вы должны использовать один, а не другой?

Ник Реталлак
источник
«Выход из вложенных циклов» является распространенной потребностью во многих языках программирования. Помимо того, что gotoв C / C ++, как упомянул @docwhat, Java пометил break и continue . (У Питона также есть отклоненное предложение для этого.)
Франклин Ю

Ответы:

104

Я думаю, что http://hasno.info/ruby-gotchas-and-caveats имеет достойное объяснение разницы:

ловить / бросать не то же самое, что поднимать / спасать. catch / throw позволяет вам быстро выходить из блоков обратно в точку, где для определенного символа определена ловушка, повышение аварийного восстановления - это реальная вещь обработки исключений, включающая объект Exception.

Readonly
источник
1
Любопытно узнать ... Читая это с iPad, не могу протестировать их в 1.9, но некоторые из этих ошибок уже не действуют в последних версиях ruby, верно?
Дени де Бернарди
12
Также стоит знать: raiseэто очень дорого. throwне является. Думайте об throwиспользовании, gotoчтобы выйти из цикла.
что
4
@Denis На какие ошибки ты ссылаешься?
что
1
Ссылка не работает!
Морхук
Посмотрите рубиновый бросок и эффективность, чтобы лучше понять разницу в производительности.
Франклин Ю.
110
  • raise, fail, rescueИ ensureручки ошибки , также известный как исключения
  • throwи catchявляются управление потоком

В отличие от других языков, throw и catch Ruby не используются для исключений. Вместо этого они предоставляют способ прекратить выполнение на ранней стадии, когда не требуется никакой дальнейшей работы. (Grimm, 2011)

Завершение одного уровня управления потоком, как whileцикл, может быть сделано с помощью простого return. Завершение многих уровней потока управления, например, вложенного цикла, может быть выполнено с помощью throw.

Несмотря на то, что механизм исключений при поднятии и спасении отлично подходит для отказа от выполнения, когда что-то идет не так, иногда приятно иметь возможность выпрыгнуть из глубоко вложенной конструкции во время обычной обработки. Это где ловить и бросать пригодится. (Томас и Хант, 2001)

Ссылки

  1. Гримм, Авди. "Бросай, лови, поднимай, спасай ... Я так растерялся!" Блог RubyLearning. Np, 11 июля 2011 года. Веб. 1 января 2012 г. http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue--im-so-confused/ .
  2. Томас, Дейв и Эндрю Хант. «Программирование на Ruby». : Прагматическое руководство программиста Np, 2001. Web. 29 сентября 2015 г. http://ruby-doc.com/docs/ProgrammingRuby/html/tut_exceptions.html .
Джаред Бек
источник
2
Авди не выглядит так, как будто он звучит в подкастах.
hrdwdmrbl
2
Ссылка Ruby Learning, похоже, не работает. Вот еще одно сообщение в блоге, в котором обсуждаются различия: danielchangnyc.github.io/blog/2013/10/23/throw-raise
Деннис
Забавно, rubylearning.com считает, что статья Авди все еще там . Я думаю, именно поэтому мы копируем материал в SO, чтобы он не был потерян!
Джаред Бек
21

https://coderwall.com/p/lhkkug/don-t-confuse-ruby-s-throw-statement-with-raise предлагает отличное объяснение, которое, я сомневаюсь, смогу улучшить. Подводя итог, прокалывая некоторые примеры кода из поста в блоге, я иду:

  1. raise/ rescueЯвляются ближайшими аналогами к throw/ catchпостроить вы знакомы с других языков (или на языке Python raise/ except). Если вы столкнулись с ошибкой, и вы throwперешли на другой язык, вы должны сделать это raiseв Ruby.

  2. 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

    в то время как несколько catches должны быть вложены ...

    catch :foo do
      catch :bar do
        do_something_that_can_throw_foo_or_bar
      end
    end
  • Голый rescueэквивалент rescue StandardErrorи идиоматическая конструкция. «Голый catch», вроде бы catch() {throw :foo}, никогда ничего не поймает и не должен использоваться.

Марк Эмери
источник
Хорошее объяснение, но напрашивается вопрос, с какой стати они спроектируют рейз в рубине = метание на другом языке. а затем также включить throw но it! = throw на других языках. Я не вижу их оригинальной логики там
wired00
@ wired00 ( пожимает плечами .) Я согласен, что это кажется довольно эксцентричным по сравнению с другими популярными языками сегодня.
Марк Эмери
2
@ wired00: С самого первого эксперимента по обработке структурированных ошибок в 1960-х годах его называли «поднятием» исключения. В оригинальных статьях, которые изобрели современную форму обработки исключений, это называется «поднятием» исключения. «Возбуждение» исключения в Lisps и Smalltalks, которые были одними из основных источников вдохновения для Ruby, и это называется «поднятие» исключения или «поднятие» аппаратного прерывания, где эта концепция существовала еще до концепции «программирования». язык "существовал. Вопрос скорее должен быть: почему эти другие языки изменили это?
Йорг Миттаг
@MarkAmery: Помните, что многие из этих «других популярных языков» моложе Ruby или, по крайней мере, современны. Таким образом, вопрос должен быть: почему эти другие языки не следуют Ruby (Smalltalk, Lisp, аппаратное обеспечение и литература).
Йорг Миттаг
@ JörgWMittag Интересно - вы вдохновили меня на небольшое историческое исследование. В C ++ было понятие «выбрасывать» исключение за годы до появления Ruby, и, согласно английскому слову .stackexchange.com / a / 449209 / 73974, термин фактически восходит к 70-м годам ... поэтому я думаю, что мы все еще будем критиковать Ruby за взятие установленной терминологии и использование ее для обозначения чего-то совершенно другого.
Марк Амери