Ruby Language
Atrapar excepciones con Begin / Rescue
Buscar..
Un bloque de manejo de error básico
Hagamos una función para dividir dos números, eso es muy confiado en su entrada:
def divide(x, y)
return x/y
end
Esto funcionará bien para muchas entradas:
> puts divide(10, 2)
5
Pero no todos
> puts divide(10, 0)
ZeroDivisionError: divided by 0
> puts divide(10, 'a')
TypeError: String can't be coerced into Fixnum
Podemos reescribir la función envolviendo la operación de división de riesgo en un bloque de begin... end
para verificar errores, y usar una cláusula de rescue
para emitir un mensaje y devolver nil
si hay un problema.
def divide(x, y)
begin
return x/y
rescue
puts "There was an error"
return nil
end
end
> puts divide(10, 0)
There was an error
> puts divide(10, 'a')
There was an error
Guardando el error
Puede guardar el error si desea utilizarlo en la cláusula de rescue
def divide(x, y)
begin
x/y
rescue => e
puts "There was a %s (%s)" % [e.class, e.message]
puts e.backtrace
end
end
> divide(10, 0)
There was a ZeroDivisionError (divided by 0)
from (irb):10:in `/'
from (irb):10
from /Users/username/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'
> divide(10, 'a')
There was a TypeError (String can't be coerced into Fixnum)
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/workspace.rb:87:in `eval'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/workspace.rb:87:in `evaluate'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/context.rb:380:in `evaluate'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:489:in `block (2 levels) in eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:623:in `signal_status'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:486:in `block in eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:246:in `block (2 levels) in each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:232:in `loop'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:232:in `block in each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:231:in `catch'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:231:in `each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:485:in `eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:395:in `block in start'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:394:in `catch'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:394:in `start'
/Users/username/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'
Comprobación de errores diferentes
Si desea hacer cosas diferentes según el tipo de error, use varias cláusulas de rescue
, cada una con un tipo de error diferente como argumento.
def divide(x, y)
begin
return x/y
rescue ZeroDivisionError
puts "Don't divide by zero!"
return nil
rescue TypeError
puts "Division only works on numbers!"
return nil
end
end
> divide(10, 0)
Don't divide by zero!
> divide(10, 'a')
Division only works on numbers!
Si desea guardar el error para utilizarlo en el bloque de rescue
:
rescue ZeroDivisionError => e
Utilice una cláusula de rescue
sin argumentos para detectar errores de un tipo no especificado en otra cláusula de rescue
.
def divide(x, y)
begin
return x/y
rescue ZeroDivisionError
puts "Don't divide by zero!"
return nil
rescue TypeError
puts "Division only works on numbers!"
return nil
rescue => e
puts "Don't do that (%s)" % [e.class]
return nil
end
end
> divide(nil, 2)
Don't do that (NoMethodError)
En este caso, tratar de dividir nil
por 2 no es un ZeroDivisionError
o un TypeError
, por lo que se maneja con la cláusula de rescue
predeterminada, que imprime un mensaje que nos permite saber que era un NoMethodError
.
Reintentando
En una cláusula de rescue
, puede usar retry
para ejecutar la cláusula de begin
nuevamente, probablemente después de cambiar la circunstancia que causó el error.
def divide(x, y)
begin
puts "About to divide..."
return x/y
rescue ZeroDivisionError
puts "Don't divide by zero!"
y = 1
retry
rescue TypeError
puts "Division only works on numbers!"
return nil
rescue => e
puts "Don't do that (%s)" % [e.class]
return nil
end
end
Si pasamos parámetros que sabemos que causarán un TypeError
, la cláusula de begin
se ejecuta (marcada aquí imprimiendo "Acerca de la división") y el error se detecta como antes, y se devuelve nil
:
> divide(10, 'a')
About to divide...
Division only works on numbers!
=> nil
Pero si pasamos parámetros que causarán un ZeroDivisionError
, se ejecuta la cláusula begin
, se captura el error, el divisor cambia de 0 a 1, y luego el retry
hace que el bloque begin
se ejecute nuevamente (desde arriba), ahora con un diferente y
La segunda vez no hay ningún error y la función devuelve un valor.
> divide(10, 0)
About to divide... # First time, 10 ÷ 0
Don't divide by zero!
About to divide... # Second time 10 ÷ 1
=> 10
Comprobando si no se produjo ningún error
Puede usar una cláusula else
para el código que se ejecutará si no se produce ningún error.
def divide(x, y)
begin
z = x/y
rescue ZeroDivisionError
puts "Don't divide by zero!"
rescue TypeError
puts "Division only works on numbers!"
return nil
rescue => e
puts "Don't do that (%s)" % [e.class]
return nil
else
puts "This code will run if there is no error."
return z
end
end
La cláusula else
no se ejecuta si hay un error que transfiere el control a una de las cláusulas de rescue
:
> divide(10,0)
Don't divide by zero!
=> nil
Pero si no se produce ningún error, la cláusula else
ejecuta:
> divide(10,2)
This code will run if there is no error.
=> 5
Tenga en cuenta que la cláusula else
no se ejecutará si regresa de la cláusula begin
def divide(x, y)
begin
z = x/y
return z # Will keep the else clause from running!
rescue ZeroDivisionError
puts "Don't divide by zero!"
else
puts "This code will run if there is no error."
return z
end
end
> divide(10,2)
=> 5
Código que siempre debe correr
Utilice un ensure
cláusula si hay código que siempre se desea ejecutar.
def divide(x, y)
begin
z = x/y
return z
rescue ZeroDivisionError
puts "Don't divide by zero!"
rescue TypeError
puts "Division only works on numbers!"
return nil
rescue => e
puts "Don't do that (%s)" % [e.class]
return nil
ensure
puts "This code ALWAYS runs."
end
end
La ensure
cláusula se ejecutará cuando hay un error:
> divide(10, 0)
Don't divide by zero! # rescue clause
This code ALWAYS runs. # ensure clause
=> nil
Y cuando no hay error:
> divide(10, 2)
This code ALWAYS runs. # ensure clause
=> 5
La cláusula de garantía es útil cuando quiere asegurarse, por ejemplo, de que los archivos están cerrados.
Tenga en cuenta que, a diferencia de la else
cláusula, el ensure
cláusula se ejecuta antes del begin
o rescue
cláusula devuelve un valor. Si el ensure
cláusula tiene un return
que va a anular el return
valor de cualquier otra cláusula!