Возращаемые значения 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 и d – return внутри 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
Будьте внимательны во время написания кода обработки ошибок и старайтесь покрывать его тестами, в том числе и на возвращаемый результат.