Ruby Language
Исключение сбоев с начала / спасения
Поиск…
Основной блок обработки ошибок
Давайте сделаем функцию для деления двух чисел, это очень доверительно относится к ее вводу:
def divide(x, y)
return x/y
end
Это будет отлично работать для большого количества ресурсов:
> puts divide(10, 2)
5
Но не все
> puts divide(10, 0)
ZeroDivisionError: divided by 0
> puts divide(10, 'a')
TypeError: String can't be coerced into Fixnum
Мы можем переписать функцию, обернув операцию рискованного деления в блоке begin... end чтобы проверить наличие ошибок, и используйте предложение rescue для вывода сообщения и возврата nil если есть проблема.
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
Сохранение ошибки
Вы можете сохранить ошибку, если хотите использовать ее в предложении 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>'
Проверка различных ошибок
Если вы хотите делать разные вещи в зависимости от типа ошибки, используйте несколько предложений о 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
end
end
> divide(10, 0)
Don't divide by zero!
> divide(10, 'a')
Division only works on numbers!
Если вы хотите сохранить ошибку для использования в блоке rescue восстановления:
rescue ZeroDivisionError => e
Используйте предложение rescue без аргумента, чтобы ловить ошибки типа, не указанного в другом предложении 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)
В этом случае, пытаясь разделить nil на 2 не ZeroDivisionError или TypeError , поэтому он обрабатывается по умолчанию rescue пункта, который печатает сообщение , чтобы сообщить нам , что это был NoMethodError .
Повторная
В предложении rescue вы можете использовать retry для retry запуска предложения begin , предположительно после изменения обстоятельства, вызвавшего ошибку.
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
Если мы передадим параметры, которые, как нам известно, вызовут TypeError , будет выполняться предложение begin (помечено здесь, распечатав «О делении»), и ошибка поймана по-прежнему, и возвращается nil :
> divide(10, 'a')
About to divide...
Division only works on numbers!
=> nil
Но если мы передаем параметры , которые вызывают ZeroDivisionError , то begin условие выполняется, то ошибка поймана, делитель изменяется от 0 до 1, а затем retry Заставляет begin блок снова запустить (сверху), теперь с разные y . Во второй раз ошибки нет, и функция возвращает значение.
> divide(10, 0)
About to divide... # First time, 10 ÷ 0
Don't divide by zero!
About to divide... # Second time 10 ÷ 1
=> 10
Проверка отсутствия ошибки
Вы можете использовать предложение else для кода, который будет запущен, если ошибка не возникнет.
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
Предложение else не выполняется, если есть ошибка, которая передает управление одному из предложений о rescue :
> divide(10,0)
Don't divide by zero!
=> nil
Но если ошибка не возникает, выполняется условие else :
> divide(10,2)
This code will run if there is no error.
=> 5
Обратите внимание, что предложение else не будет выполнено, если вы вернетесь из предложения 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
Код, который должен всегда запускаться
Используйте ensure положение , если есть код , который вы всегда хотите выполнить.
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
Предложение ensure будет выполняться при возникновении ошибки:
> divide(10, 0)
Don't divide by zero! # rescue clause
This code ALWAYS runs. # ensure clause
=> nil
И когда нет ошибки:
> divide(10, 2)
This code ALWAYS runs. # ensure clause
=> 5
Предложение обеспечения полезно, если вы хотите убедиться, например, что файлы закрыты.
Обратите внимание, что в отличие от предложения else предложение ensure выполняется до того, как предложение begin или rescue возвращает значение. Если условие ensure имеет return , который переопределит return значение любого другого предложения!