GitHub

Tensho

Заметки непутевого программиста

Возращаемые значения rescue/ensure в Ruby

24/01/2018

Иногда бывают такие дни, когда хочется просто поэксперементировать с нестандартным кодом на языке программирования, которым зарабатываешь на хлеб. Сегодня я бы хотел поделиться некоторыми ньюансами и неочевидными поведениями rescue и ensure конструкций Ruby. Напомню, что в Ruby результат последнего выражения внутри метода или begin/end блока возвращается неявно, и это считается хорошей практикой. Однако, можно и явно указывать return и возвращать результат, например, досрочно. Это играет определенную роль в поведении rescue и ensure, поэтому я буду отдельно проверять поведение при явном и неявном возврате результата.

def a
  raise
  1
rescue
  2
end
#=> :a
a
#=> 2 ---> Стандартное неявное возвращение результата из rescue

def b
  raise
  1
rescue
  return 2
end
#=> :b
b
#=> 2 ---> Стандартное явное возвращение результата из rescue

def c
  raise
  1
rescue
  return 2
ensure
  3
end
#=> :x
c
#=> 2 ---> ensure не перекрывает результат rescue

def d
  raise
  1
rescue
  return 2
ensure
  return 3
end
#=> :d
d
#=> 3 ---> ensure перекрывает результат rescue

Думаю вы заметили разницу между методами c и dreturn внутри ensure подменяет результат из resuce. Лично меня пример метода d каждый раз приводит в легкое удивление. Давайте теперь посмотрим, что происходит, если внутри rescue или ensure вызывается исключение.

def e
  raise
rescue
  raise 'A'
end
#=> :e
e
#=> RuntimeError: A ---> Ожидаемо вываливаемся с исключением 'A'

def f
  raise
ensure
  raise 'B'
end
#=> :f
f
#=> RuntimeError: B ---> Ожидаемо ли вываливаемся с исключением 'B'?

def g
  raise
rescue
  raise 'A'
ensure
  raise 'B'
end
#=> :g
g
#=> RuntimeError: B ---> Опаньки, исключение 'A' было проглочено исключением 'B'

def h
  raise
rescue
  raise 'A'
ensure
end
#=> :h
h
#=> RuntimeError: A ---> Ожидаемо вываливаемся с исключением 'A'

def i
  raise
rescue
  raise 'A'
ensure
  return 3
end
#=> :i
i
#=> 3 ---> Опаньки, исключение 'A' было проглочено ensure как и в примере g

Будьте внимательны во время написания кода обработки ошибок и старайтесь покрывать его тестами, в том числе и на возвращаемый результат.