e` en Ruby?'/> e` en Ruby?'/> e` en Ruby?'/> ¿Por qué es malo estilo 'rescatar Exception => e` en Ruby? | programandonet.com

Pregunta ¿Por qué es malo estilo 'rescatar Exception => e` en Ruby?


Ryan Davis Ruby QuickRef dice (sin explicación):

No rescates la excepción. NUNCA. o te apuñalaré.

Por qué no? ¿Qué es lo correcto?


800
2018-04-06 19:17


origen


Respuestas:


TL; DR: Utilizar StandardError en lugar de captura de excepción general. Cuando se vuelve a generar la excepción original (por ejemplo, al rescatar para registrar solo la excepción), rescatar Exception probablemente esté bien


Exception es la raíz de Jerarquía de excepciones de RubyEntonces, cuando rescue Exception rescatas de todo, incluidas subclases como SyntaxError, LoadErrory Interrupt.

Rescatando Interrupt impide que el usuario use CTRLdo para salir del programa

Rescatando SignalException impide que el programa responda correctamente a las señales. Será inacabable excepto por kill -9.

Rescatando SyntaxError significa que evals que fallan lo harán en silencio.

Todos estos se pueden mostrar ejecutando este programa y tratando de CTRLdo o kill eso:

loop do
  begin
    sleep 1
    eval "djsakru3924r9eiuorwju3498 += 5u84fior8u8t4ruyf8ihiure"
  rescue Exception
    puts "I refuse to fail or be stopped!"
  end
end

Rescatando de Exception ni siquiera es el predeterminado. Obra

begin
  # iceberg!
rescue
  # lifeboats
end

no rescata de Exception, rescata de StandardError. En general, debe especificar algo más específico que el predeterminado StandardError, pero rescatando de Exception  ensancha el alcance en lugar de reducirlo, y puede tener resultados catastróficos y hacer que la búsqueda de errores sea extremadamente difícil.


Si tiene una situación en la que desea rescatar StandardError y necesita una variable con la excepción, puede usar esta forma:

begin
  # iceberg!
rescue => e
  # lifeboats
end

que es equivalente a:

begin
  # iceberg!
rescue StandardError => e
  # lifeboats
end

Uno de los pocos casos comunes donde es sensato rescatar Exception es para fines de registro / informes, en cuyo caso debe volver a plantear la excepción de inmediato:

begin
  # iceberg?
rescue Exception => e
  # do some logging
  raise e  # not enough lifeboats ;)
end

1240
2018-04-06 19:38



los real regla es: no deseche las excepciones. La objetividad del autor de su cita es cuestionable, como lo demuestra el hecho de que termina con

o te apuñalaré

Por supuesto, tenga en cuenta que las señales (por defecto) arrojan excepciones, y normalmente los procesos de larga duración terminan a través de una señal, por lo que capturar Exception y no terminar en excepciones de señal hará que su programa sea muy difícil de detener. Entonces no hagas esto

#! /usr/bin/ruby

while true do
  begin
    line = STDIN.gets
    # heavy processing
  rescue Exception => e
    puts "caught exception #{e}! ohnoes!"
  end
end

No, realmente, no lo hagas. Ni siquiera corras para ver si funciona.

Sin embargo, supongamos que tiene un servidor enhebrado y desea que todas las excepciones no:

  1. ser ignorado (el predeterminado)
  2. detener el servidor (lo que sucede si dices thread.abort_on_exception = true)

Entonces esto es perfectamente aceptable en su hilo de manejo de conexión:

begin
  # do stuff
rescue Exception => e
  myLogger.error("uncaught #{e} exception while handling connection: #{e.message}")
    myLogger.error("Stack trace: #{backtrace.map {|l| "  #{l}\n"}.join}")
end

Lo anterior funciona con una variación del manejador de excepciones predeterminado de Ruby, con la ventaja de que no mata tu programa. Rails hace esto en su controlador de solicitud.

Las excepciones de señal se generan en el hilo principal. Los hilos de fondo no los obtendrán, por lo que no tiene sentido tratar de atraparlos allí.

Esto es particularmente útil en un entorno de producción, donde lo hace no quiere que su programa simplemente se detenga cada vez que algo sale mal. Luego puede tomar los volcados de pila en sus registros y agregarlos a su código para hacer frente a una excepción específica más adelante en la cadena de llamadas y de una manera más elegante.

Tenga en cuenta también que hay otro idioma Ruby que tiene el mismo efecto:

a = do_something rescue "something else"

En esta línea, si do_something plantea una excepción, es atrapada por Ruby, desechada, y a es asignado "something else".

Generalmente, no hagas eso, excepto en casos especiales donde saber no necesitas preocuparte Un ejemplo:

debugger rescue nil

los debugger función es una forma bastante agradable de establecer un punto de interrupción en el código, pero si se ejecuta fuera de un depurador y Rails, se genera una excepción. Ahora, teóricamente, no deberías dejar el código de depuración en tu programa (¡por favor! ¡Nadie lo hace!) Pero es posible que quieras mantenerlo allí por un tiempo por algún motivo, pero no ejecutar continuamente tu depurador.

Nota:

  1. Si ha ejecutado el programa de otra persona que capta excepciones de señal y las ignora, (diga el código anterior), entonces:

    • en Linux, en un shell, escriba pgrep ruby, o ps | grep ruby, busque el PID de su programa ofensivo y luego ejecútelo kill -9 <PID>.
    • en Windows, use el Administrador de tareas (CTRL-CAMBIO-ESC), vaya a la pestaña "procesos", encuentre su proceso, haga clic derecho y seleccione "Terminar proceso".
  2. Si está trabajando con el programa de otra persona que, por alguna razón, está salpicado con estos bloques de ignorar excepción, entonces poner esto en la parte superior de la línea principal es un posible escape:

    %W/INT QUIT TERM/.each { |sig| trap sig,"SYSTEM_DEFAULT" }
    

    Esto hace que el programa responda a las señales de terminación normales al finalizar de forma inmediata, pasando por alto a los manejadores de excepciones, sin limpieza. Por lo tanto, podría causar la pérdida de datos o similar. ¡Ten cuidado!

  3. Si necesitas hacer esto:

    begin
      do_something
    rescue Exception => e
      critical_cleanup
      raise
    end
    

    puedes hacer esto:

    begin
      do_something
    ensure
      critical_cleanup
    end
    

    En el segundo caso, critical cleanup se llamará cada vez, independientemente de si se lanza una excepción.


73
2018-04-07 05:30



Digamos que estás en un auto (corriendo Ruby). Recientemente instaló un nuevo volante con el sistema de actualización por aire (que usa eval), pero no sabías que uno de los programadores se equivocó con la sintaxis.

Estás en un puente y te das cuenta de que vas un poco hacia la barandilla, por lo que debes girar a la izquierda.

def turn_left
  self.turn left:
end

Uy! Eso es probablemente No está bien, afortunadamente, Ruby levanta un SyntaxError.

El auto debe detenerse inmediatamente, ¿no?

Nop.

begin
  #...
  eval self.steering_wheel
  #...
rescue Exception => e
  self.beep
  self.log "Caught #{e}.", :warn
  self.log "Logged Error - Continuing Process.", :info
end

beep beep

Advertencia: Capturada la excepción SyntaxError.

Información: Error registrado - Proceso continuo.

Observa que algo anda mal y golpea los descansos de emergencia (^C: Interrupt)

beep beep

Advertencia: excepción de interrupción atrapada.

Información: Error registrado - Proceso continuo.

Sí, eso no ayudó mucho. Estás muy cerca de la barandilla, así que pones el auto en el parque (killEn g: SignalException)

beep beep

Advertencia: se ha detectado la excepción SignalException.

Información: Error registrado - Proceso continuo.

En el último segundo, sacas las llaves (kill -9), y el automóvil se detiene, se golpea contra el volante (el airbag no se puede inflar porque no detuvo el programa con gracia, lo terminó), y la computadora en la parte trasera de su auto se estrella contra el asiento de frente a eso. Una lata medio llena de Coca-Cola se derrama sobre los papeles. Los comestibles en la parte posterior están aplastados, y la mayoría están cubiertos de yema de huevo y leche. El automóvil necesita reparación y limpieza serias. (Pérdida de datos)

Espero que tengas seguro (copias de seguridad). Ah, sí, porque el airbag no se infló, probablemente te lastimes (te despidan, etc.).


¡Pero espera! Hay Más razones por las que es posible que desee utilizar rescue Exception => e!

Digamos que eres ese auto y quieres asegurarte de que el airbag se infla si el auto va más de 5 mph antes de detenerse.

 begin 
    # do driving stuff
 rescue Exception => e
    self.airbags.inflate if self.speed >= 5.mph 
    raise
 end

Aquí está la excepción a la regla: puedes atrapar Exception  solo si vuelves a plantear la excepción. Entonces, una regla mejor es nunca tragar Exceptiony siempre vuelve a subir el error

Pero agregar rescate es fácil de olvidar en un lenguaje como Ruby, y poner una declaración de rescate justo antes de volver a plantear un problema parece un poco no SECO. Y tu no haga quiero olvidar el raise declaración. Y si lo haces, buena suerte tratando de encontrar ese error.

Afortunadamente, Ruby es increíble, puedes usar el ensure palabra clave, que asegura que el código se ejecuta. los ensure la palabra clave ejecutará el código sin importar qué, si se lanza una excepción, si no lo es, siendo la única excepción si el mundo finaliza (u otros eventos poco probables).

 begin 
    # do driving stuff
 ensure
    self.airbags.inflate if self.speed >= 5.mph 
 end

¡Auge! Y ese código debería funcionar de todos modos. La única razón por la que debes usar rescue Exception => e es si necesita acceder a la excepción, o si solo desea que el código se ejecute en una excepción. Y recuerda volver a subir el error. Cada vez. O tendrá 3 personas apuñalándolo (incluido su jefe).


TL; DR

No lo hagas rescue Exception => e (y no vuelva a subir la excepción) - o usted podría conducir por un puente.


46
2018-01-31 23:55



Porque esto captura todas las excepciones. Es poco probable que su programa pueda recuperarse de alguna de ellos.

Solo debe manejar excepciones de las que sepa cómo recuperarse. Si no anticipa un cierto tipo de excepción, no la maneje, cuelgue fuerte (escriba detalles en el registro), luego diagnostique los registros y corrija el código.

Tragar excepciones es malo, no hagas esto.


43
2018-04-06 19:21



Ese es un caso específico de la regla de que no debes atrapar alguna excepción que no sabes cómo manejar. Si no sabe cómo manejarlo, siempre es mejor dejar que otra parte del sistema lo detecte y maneje.


8
2018-04-06 23:54



Esto también le ocultará errores, por ejemplo si escribió mal el nombre de un método:

def my_fun
  "my_fun"
end

begin
 # you mistypped my_fun to my_func
 my_func # my_func()
rescue Exception
  # rescued NameError (or NoMethodError if you called method with parenthesis)
end

1
2018-03-25 09:11