Qual é a diferença entre Raising Exceptions vs Throwing Exceptions in Ruby?

168

O Ruby possui dois mecanismos de exceção diferentes: Throw / Catch e Raise / Rescue.

Por que nós temos dois?

Quando você deve usar um e não o outro?

Nick Retallack
fonte
“Sair de loops aninhados” é uma necessidade comum em muitas linguagens de programação. Além disso o gotoem C / C ++ como @docwhat mencionou, Java tem marcado ruptura e continuar . (Python também tem uma proposta rejeitada para isso).
Franklin Yu

Respostas:

104

Eu acho que http://hasno.info/ruby-gotchas-and-caveats tem uma explicação decente da diferença:

pegar / arremessar não é o mesmo que levantar / resgatar. catch / throw permite que você saia rapidamente dos blocos de volta a um ponto em que uma captura é definida para um símbolo específico;

Somente leitura
fonte
1
Curioso para saber ... Lendo isso de um iPad, não podemos testá-los no 1.9, mas alguns desses truques não são mais válidos nas versões recentes do ruby, certo?
Denis de Bernardy
12
Também vale a pena conhecer: raiseé muito caro. thrownão é. Pense throwcomo usar gotopara sair de um loop.
docwhat
4
@ Denis A que dicas você está se referindo?
docwhat
1
O link está quebrado!
morhook
Veja ruby catch-throw e eficiência para entender mais sobre a diferença de desempenho.
Franklin Yu
110
  • raise, fail, rescue, E ensurelidar com erros , também conhecido como exceções
  • throwe catchsão fluxo de controle

Diferentemente de outros idiomas, o throw e catch de Ruby não são usados ​​para exceções. Em vez disso, eles fornecem uma maneira de encerrar a execução mais cedo, quando nenhum trabalho adicional é necessário. (Grimm, 2011)

O término de um único nível de fluxo de controle, como um whileloop, pode ser feito com um simples return. É possível encerrar muitos níveis de fluxo de controle, como um loop aninhado throw.

Embora o mecanismo de exceção de aumento e resgate seja ótimo para abandonar a execução quando algo der errado, às vezes é bom poder pular de uma construção profundamente aninhada durante o processamento normal. É aqui que o catch and throw é útil. (Thomas e Hunt, 2001)

Referências

  1. Grimm, Avdi. "Jogue, pegue, levante, resgate ... estou tão confusa!" Blog do RubyLearning. Np, 11 de julho de 2011. Web. 1 de janeiro de 2012. http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue--im-so-confused/ .
  2. Thomas, Dave e Andrew Hunt. "Programando Ruby." : Guia Pragmático do Programador. Np, 2001. Web. 29 de setembro de 2015. http://ruby-doc.com/docs/ProgrammingRuby/html/tut_exceptions.html .
Jared Beck
fonte
2
Avdi não parece que ele soa em podcasts.
Hrdwdmrbl 28/05
2
O link do Ruby Learning parece não funcionar. Aqui está outro post que discute as diferenças: danielchangnyc.github.io/blog/2013/10/23/throw-raise
Dennis
Engraçado, o rubylearning.com acha que o artigo de Avdi ainda está lá . Acho que é por isso que copiamos as coisas para o SO, para que não se percam!
Jared Beck
21

https://coderwall.com/p/lhkkug/don-t-confuse-ruby-s-throw-statement-with-raise oferece uma excelente explicação que duvido que possa melhorar. Para resumir, copiando alguns exemplos de código da postagem do blog enquanto eu vou:

  1. raise/ rescuesão os análogos mais próximos da construção throw/ que catchvocê conhece de outras linguagens (ou da raise/ do Python except). Se você encontrou uma condição de erro e a encontrou throwem outro idioma, deve fazê-lo raiseem Ruby.

  2. Ruby throw/ catchpermite interromper a execução e subir na pilha procurando por catch(como raise/ rescuefaz), mas não é realmente destinado a condições de erro. Ele deve ser usado raramente e existe apenas para quando o catchcomportamento "subir a pilha até encontrar um correspondente " faz sentido para um algoritmo que você está escrevendo, mas não faria sentido pensar nele throwcomo correspondente a um erro doença.

    Para que o catch and throw é usado no Ruby? oferece algumas sugestões sobre bons usos do throw/ catchconstruct.

As diferenças comportamentais concretas entre eles incluem:

  • rescue Fooresgatará instâncias de Fooinclusão de subclasses de Foo. catch(foo)só pegará o mesmo objetoFoo . Não apenas você não pode passar catchum nome de classe para capturar instâncias, como também não fará comparações de igualdade. Por exemplo

    catch("foo") do
      throw "foo"
    end

    lhe dará um UncaughtThrowError: uncaught throw "foo"(ou um ArgumentErrornas versões do Ruby anteriores à 2.2)

  • Várias cláusulas de resgate podem ser listadas ...

    begin
      do_something_error_prone
    rescue AParticularKindOfError
      # Insert heroism here.
    rescue
      write_to_error_log
      raise
    end

    enquanto vários catches precisam ser aninhados ...

    catch :foo do
      catch :bar do
        do_something_that_can_throw_foo_or_bar
      end
    end
  • Um bare rescueé equivalente rescue StandardErrore é uma construção idiomática. Um "nu catch", como catch() {throw :foo}, nunca vai pegar nada e não deve ser usado.

Mark Amery
fonte
Boa explicação, mas implora a pergunta: por que diabos eles projetariam o aumento em ruby ​​= throw em outro idioma? e também inclua throw but it! = throw em outros idiomas. Eu não posso ver a sua lógica original lá
wired00
@ wired00 ( Encolho de ombros.) Concordo que parece bastante excêntrico em comparação com outros idiomas populares hoje em dia.
Mark Amery
2
@ wired00: Tem sido chamado "levantando" uma exceção desde os primeiros experimentos com manipulação de erros estruturados na década de 1960, é chamado "levantando" uma exceção nos artigos seminais que inventaram a forma moderna de tratamento de exceções, é chamado "levantando" uma exceção em Lisps e Smalltalks, que foram algumas das principais inspirações para Ruby, e é chamado "levantando" uma exceção ou "levantando" uma interrupção no hardware, onde o conceito existia antes do conceito de "programação" idioma "existia. A pergunta deveria ser: por que essas outras línguas mudaram isso?
Jörg W Mittag
@ MarkAmery: Lembre-se de que muitas dessas "outras línguas populares" são mais jovens que Ruby ou pelo menos contemporâneas. Portanto, a pergunta deveria ser: por que essas outras línguas não seguiram Ruby (e Smalltalk e Lisp, hardware e literatura).
Jörg W Mittag
@ JörgWMittag Interessante - você me inspirou a fazer uma pequena pesquisa histórica. C ++ teve a noção de "lançar" uma exceção anos antes de Ruby aparecer, e por english.stackexchange.com/a/449209/73974 o termo realmente remonta aos anos 70 ... então acho que ainda podemos criticar Ruby por tomando terminologia estabelecida e usando-a para significar algo completamente diferente.
Mark Amery