2011-06-08 17 views
40

Estoy tratando de entender las excepciones en Ruby pero estoy un poco confundido. El tutorial estoy usando dice que si se produce una excepción que no coincide con ninguna de las excepciones señaladas por las declaraciones de rescate, se puede utilizar una "cosa" para cogerlo:Ruby Exceptions - ¿Por qué "else"?

begin 
# - 
rescue OneTypeOfException 
# - 
rescue AnotherTypeOfException 
# - 
else 
# Other exceptions 
ensure 
# Always will be executed 
end 

Sin embargo, también vi más tarde en el tutorial de "rescate" se utilizan sin excepción especificada:

begin 
    file = open("/unexistant_file") 
    if file 
     puts "File opened successfully" 
    end 
rescue 
    file = STDIN 
end 
print file, "==", STDIN, "\n" 

Si usted puede hacer esto, entonces hacer yo jamás necesidad de usar otro sitio? ¿O puedo usar un rescate genérico al final como este?

begin 
# - 
rescue OneTypeOfException 
# - 
rescue AnotherTypeOfException 
# - 
rescue 
# Other exceptions 
ensure 
# Always will be executed 
end 
+4

¿Cuál es el tutorial, así que sé que no recomendaría? –

+4

@AndrewGrimm Después de buscar en Google, creo que el tutorial que el asker estaba siguiendo era http://www.tutorialspoint.com/ruby/ruby_exceptions.htm.Bonificación WTF: el autor de ese tutorial parece haber plagiado el primer ejemplo de http://rubylearning.com/satishtalim/ruby_exceptions.html (donde ya era incorrecto), pero lo empeoró aún más atornillando la sangría de los comentarios . Y sí, creo que sería prudente alejar a la gente de él: ¡qué gran combinación de incompetencia * y * deshonestidad en exhibición! –

Respuesta

76

El else es para cuando se completa el bloque sin una excepción lanzada. El ensure se ejecuta si el bloque se completa correctamente o no. Ejemplo:

begin 
    puts "Hello, world!" 
rescue 
    puts "rescue" 
else 
    puts "else" 
ensure 
    puts "ensure" 
end 

Esto imprimirá Hello, world!, entonces else, entonces ensure.

+2

¿Por qué incluir la porción else en el bloque begin? –

+0

@AntarrByrd En Ruby, 'begin' es como' try' en otros idiomas. El 'else', aquí, significa, haz esto si no se han lanzado excepciones en ese bloque' begin' ('try'). –

+0

Pero si el código en el bloque begin no arroja un error. Puedes continuar allí ya que ese es el único caso en el que se ejecutará. –

1

El bloque else en un bloque final de rescate de inicio se usa cuando tal vez espere una excepción de algún tipo. Si ejecuta todas sus excepciones esperadas pero todavía no ha levantado nada, entonces en su bloque else puede hacer lo que sea necesario ahora que sabe que su código original funcionó sin errores.

4

He aquí un caso de uso concreto para else en una expresión begin. Supongamos que está escribiendo pruebas automatizadas y desea escribir un método que devuelva el error generado por un bloque. Pero también quiere que la prueba falle si el bloque no genera un error. Usted puede hacer esto:

def get_error_from(&block) 
    begin 
    block.call 
    rescue => err 
    err # we want to return this 
    else 
    raise "No error was raised" 
    end 
end 

Tenga en cuenta que no se puede mover el raise dentro del bloque begin, porque va a llegar rescue d. Por supuesto, hay otras maneras sin usar else, como comprobar si err es nil después del end, pero eso no es tan breve.

Personalmente, rara vez utilizo else de esta manera porque creo que rara vez es necesario, pero es útil en esos casos excepcionales.

+0

¿Por qué no puedes simplemente 'raise'? No se ha producido ningún error "' después del bloque 'begin rescue end'? ¿No sería exactamente lo mismo que hacerlo dentro de ese 'else'? – Magne

+0

@Magne La única forma de que funcione es si * también * utiliza un 'retorno' temprano, es decir' devuelve err' en lugar de 'err'. Si no lo hace, el método no puede devolver el error del bloque porque tiene un aumento incondicional al final del método. Por lo general, trato de evitar el 'return's temprano/explícito (excepto en el estilo guard-clause) porque es más fácil de seguir; por eso la sintaxis 'else' es más atractiva. – Kelvin

+0

ah, no me di cuenta de que el 'begin rescue end' estaba dentro de un método en el que desea devolver el error. Pero, considere mi pregunta si no necesita devolver el error (es decir, una versión independiente 'begin rescue end'). – Magne

0

La única razón que puedo ver para el bloque else es si desea ejecutar algo antes del bloque ensure cuando el código en el bloque begin no generó ningún error.

begin 
    puts "Hello" 
rescue 
    puts "Error" 
else 
    puts "Success" 
ensure 
    puts "my old friend" 
    puts "I've come to talk with you again." 
end 
0

Gracias a else a veces se puede fusionar dos begin end bloques anidados. Así
(ejemplo simplificado de mi código actual) en lugar de:

begin 
    html = begin 
     NetHTTPUtils.request_data url 
    rescue NetHTTPUtils::Error => e 
     raise unless 503 == e.code 
     sleep 60 
     retry 
    end 
    redo unless html["market"] 
    end 

se escribe:

begin 
    html = NetHTTPUtils.request_data url 
    rescue NetHTTPUtils::Error => e 
    raise unless 503 == e.code 
    sleep 60 
    retry 
    else 
    redo unless html["market"] 
    end