"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.)
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.
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.
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)
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.
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.
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
rescueAParticularKindOfError# 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
endend
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.
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.
gotoen C / C ++ como @docwhat ha mencionado, Java ha etiquetado romper y continuar . (Python también tiene una propuesta rechazada para esto.)Respuestas:
Creo que http://hasno.info/ruby-gotchas-and-caveats tiene una explicación decente de la diferencia:
fuente
raisees muy caro.throwno es. Piensethrowcomo usargotopara salir de un bucle.raise,fail,rescue, Yensuremango errores , también conocidas como excepcionesthrowycatchson control de flujoTerminar un solo nivel de flujo de control, como un
whilebucle, se puede hacer con un simplereturn. Se puede terminar con muchos niveles de flujo de control, como un bucle anidadothrow.Referencias
fuente
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:
raise/rescueson los análogos más cercanos athrow/catchconstrucción con la que está familiarizado desde otros idiomas (o alraise/ de Pythonexcept). Si ha encontrado una condición de error y la superaríathrowen otro idioma, debería hacerloraiseen Ruby.Ruby
throw/catchte permite interrumpir la ejecución y subir la pila buscando uncatch(comoraise/rescuedoes), pero en realidad no está destinado a condiciones de error. Debería usarse raramente, y está ahí solo cuando elcatchcomportamiento de "caminar por la pila hasta encontrar el correspondiente " tiene sentido para un algoritmo que está escribiendo, pero no tendría sentido pensarthrowque 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 Foorescatará instancias deFooincluir subclases deFoo.catch(foo)sólo coger el mismo objeto,Foo. No solo no puede pasarcatchun nombre de clase para capturar instancias de él, sino que ni siquiera hará comparaciones de igualdad. Por ejemplole dará un
UncaughtThrowError: uncaught throw "foo"(o unArgumentErroren versiones de Ruby anteriores a 2.2)Se pueden enumerar múltiples cláusulas de rescate ...
mientras múltiple
catcheses necesitan estar anidados ...Un desnudo
rescuees equivalenterescue StandardErrory es una construcción idiomática. Un "desnudocatch", comocatch() {throw :foo}, nunca atrapará nada y no debe usarse.fuente