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
значение любого другого предложения!