"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.
goto
en 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
raise
es muy caro.throw
no es. Piensethrow
como usargoto
para salir de un bucle.raise
,fail
,rescue
, Yensure
mango errores , también conocidas como excepcionesthrow
ycatch
son control de flujoTerminar un solo nivel de flujo de control, como un
while
bucle, 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
/rescue
son los análogos más cercanos athrow
/catch
construcción con la que está familiarizado desde otros idiomas (o alraise
/ de Pythonexcept
). Si ha encontrado una condición de error y la superaríathrow
en otro idioma, debería hacerloraise
en Ruby.Ruby
throw
/catch
te permite interrumpir la ejecución y subir la pila buscando uncatch
(comoraise
/rescue
does), pero en realidad no está destinado a condiciones de error. Debería usarse raramente, y está ahí solo cuando elcatch
comportamiento de "caminar por la pila hasta encontrar el correspondiente " tiene sentido para un algoritmo que está escribiendo, pero no tendría sentido pensarthrow
que corresponde a un error condición.¿Para qué se usa el atrapar y lanzar en Ruby?ofrece algunas sugerencias sobre buenos usos de
throw
/catch
construct.Las diferencias de comportamiento concretas entre ellos incluyen:
rescue Foo
rescatará instancias deFoo
incluir subclases deFoo
.catch(foo)
sólo coger el mismo objeto,Foo
. No solo no puede pasarcatch
un 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 unArgumentError
en versiones de Ruby anteriores a 2.2)Se pueden enumerar múltiples cláusulas de rescate ...
mientras múltiple
catch
eses necesitan estar anidados ...Un desnudo
rescue
es equivalenterescue StandardError
y es una construcción idiomática. Un "desnudocatch
", comocatch() {throw :foo}
, nunca atrapará nada y no debe usarse.fuente