¿Cuál es la diferencia entre aumentar excepciones y lanzar excepciones en Ruby?

168

Ruby tiene dos mecanismos de excepción diferentes: Lanzar / Atrapar y Elevar / Rescatar.

¿Por qué tenemos dos?

¿Cuándo deberías usar uno y no el otro?

Nick Retallack
fuente
"Salir de bucles anidados" es una necesidad común en muchos lenguajes de programación. Además de gotoen C / C ++ como @docwhat ha mencionado, Java ha etiquetado romper y continuar . (Python también tiene una propuesta rechazada para esto.)
Franklin Yu

Respuestas:

104

Creo que http://hasno.info/ruby-gotchas-and-caveats tiene una explicación decente de la diferencia:

atrapar / lanzar no es lo mismo que elevar / rescatar. catch / throw le permite salir rápidamente de los bloques de regreso a un punto donde se define un catch para un símbolo específico, elevar el rescate es la verdadera excepción que maneja cosas que involucran el objeto Exception.

Solo lectura
fuente
1
Curioso de saber ... Leer esto desde un iPad, así que no puedo probarlos en 1.9, pero algunas de esas trampas ya no son válidas en las últimas versiones de ruby, ¿verdad?
Denis de Bernardy
12
También vale la pena saberlo: raisees muy caro. throwno es. Piense throwcomo usar gotopara salir de un bucle.
docwhat
44
@Denis ¿A qué trucos te refieres?
docwhat
1
¡El enlace está roto!
morhook
Vea Ruby catch-throw y eficiencia para comprender más sobre la diferencia de rendimiento.
Franklin Yu
110
  • raise, fail, rescue, Y ensuremango errores , también conocidas como excepciones
  • throwy catchson control de flujo

A diferencia de otros idiomas, el lanzamiento y la captura de Ruby no se usan para excepciones. En cambio, proporcionan una forma de terminar la ejecución antes de tiempo cuando no se necesita más trabajo. (Grimm, 2011)

Terminar un solo nivel de flujo de control, como un whilebucle, se puede hacer con un simple return. Se puede terminar con muchos niveles de flujo de control, como un bucle anidado throw.

Si bien el mecanismo de excepción de elevar y rescatar es excelente para abandonar la ejecución cuando las cosas salen mal, a veces es bueno poder saltar de una construcción profundamente anidada durante el procesamiento normal. Aquí es donde atrapar y lanzar son útiles. (Thomas y Hunt, 2001)

Referencias

  1. Grimm, Avdi. "Lanzar, atrapar, elevar, rescatar ... ¡Estoy tan confundido!" Blog de RubyLearning. Np, 11 de julio de 2011. Web. 1 de enero de 2012. http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue--im-so-confused/ .
  2. Thomas, Dave y Andrew Hunt. "Programando Ruby". : La guía del programador pragmático. Np, 2001. Web. 29 de septiembre de 2015. http://ruby-doc.com/docs/ProgrammingRuby/html/tut_exceptions.html .
Jared Beck
fuente
2
Avdi no parece que suene en podcasts.
hrdwdmrbl
2
El enlace Ruby Learning no parece funcionar. Aquí hay otra publicación de blog que analiza las diferencias: danielchangnyc.github.io/blog/2013/10/23/throw-raise
Dennis
Divertido, rubylearning.com piensa que el artículo de Avdi todavía está allí . Supongo que es por eso que copiamos cosas a SO, para que no se pierdan.
Jared Beck
21

https://coderwall.com/p/lhkkug/don-t-confuse-ruby-s-throw-statement-with-raise ofrece una excelente explicación que dudo que pueda mejorar. Para resumir, cortando algunos ejemplos de código de la publicación del blog a medida que avanzo:

  1. raise/ rescueson los análogos más cercanos a throw/catch construcción con la que está familiarizado desde otros idiomas (o al raise/ de Python except). Si ha encontrado una condición de error y la superaría throwen otro idioma, debería hacerlo raiseen Ruby.

  2. Ruby throw/catch te permite interrumpir la ejecución y subir la pila buscando un catch(como raise/ rescuedoes), pero en realidad no está destinado a condiciones de error. Debería usarse raramente, y está ahí solo cuando el catchcomportamiento de "caminar por la pila hasta encontrar el correspondiente " tiene sentido para un algoritmo que está escribiendo, pero no tendría sentido pensar throwque corresponde a un error condición.

    ¿Para qué se usa el atrapar y lanzar en Ruby?ofrece algunas sugerencias sobre buenos usos de throw/ catchconstruct.

Las diferencias de comportamiento concretas entre ellos incluyen:

  • rescue Foo rescatará instancias de Foo incluir subclases de Foo. catch(foo)sólo coger el mismo objeto,Foo . No solo no puede pasar catchun nombre de clase para capturar instancias de él, sino que ni siquiera hará comparaciones de igualdad. Por ejemplo

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

    le dará un UncaughtThrowError: uncaught throw "foo"(o un ArgumentErroren versiones de Ruby anteriores a 2.2)

  • Se pueden enumerar múltiples cláusulas de rescate ...

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

    mientras múltiple catch eses necesitan estar anidados ...

    catch :foo do
      catch :bar do
        do_something_that_can_throw_foo_or_bar
      end
    end
  • Un desnudo rescuees equivalente rescue StandardErrory es una construcción idiomática. Un "desnudo catch", como catch() {throw :foo}, nunca atrapará nada y no debe usarse.

Mark Amery
fuente
Una buena explicación, pero plantea la pregunta, ¿por qué demonios diseñarían raise en ruby ​​= throw en otro idioma? y luego también incluye throw but it! = throw en otros idiomas. No puedo ver su lógica original allí
wired00
@ wired00 (Shrug.) Estoy de acuerdo en que parece bastante excéntrico en comparación con otros idiomas populares de hoy.
Mark Amery
2
@ wired00: Se ha llamado "elevar" una excepción desde los primeros experimentos con el manejo estructurado de errores en la década de 1960, se llama "elevar" una excepción en los artículos seminales que inventaron la forma moderna de manejo de excepciones, se llama "plantear" una excepción en Lisps y Smalltalks, que fueron algunas de las principales inspiraciones para Ruby, y se llama "plantear" una excepción o "plantear" una interrupción en el hardware, donde el concepto existía incluso antes del concepto de "programación" el lenguaje "existió. La pregunta debería ser: ¿por qué esos otros idiomas cambiaron eso?
Jörg W Mittag
@ MarkAmery: Recuerda que muchos de esos "otros idiomas populares" son más jóvenes que Ruby o al menos contemporáneos. Entonces, la pregunta debería ser: ¿por qué esos otros idiomas no siguieron a Ruby (y Smalltalk y Lisp y el hardware y la literatura)?
Jörg W Mittag
@ JörgWMittag Interesante: me inspiraste para hacer un poco de investigación histórica. C ++ tenía la noción de "lanzar" una excepción años antes de que apareciera Ruby, y según english.stackexchange.com/a/449209/73974 el término en realidad se remonta a los años 70 ... así que creo que todavía podemos criticar a Ruby por tomando una terminología establecida y usándola para significar algo completamente diferente.
Mark Amery