Руби не «обеспечивает», когда я «повторяю попытку» в «спасении»

Рассмотрим этот блок start-rescue-ensure:

attempts=0
begin
  make_service_call()
rescue Exception
  retry unless attempts>2
  exit -1
ensure
  attemps += 1
end

Если вы запустите этот код как есть, он вызовет исключение, поскольку в нем нет функции с именем make_service_call(). Итак, он повторяет попытку. Но он застрял бы в бесконечном цикле, потому что элемент управления никогда не переходит к «обеспечению» из-за «повторной попытки». Не должна ли «обеспечить» часть блока гарантировать, что код в ней будет выполнен независимо от того, что происходит в «начале» или «спасении»?

Конечно, я могу увеличить счет в «начале» — это не главное. Я просто задаю вопрос об «убедиться», чтобы получить некоторую ясность.


person harithski    schedule 03.06.2011    source источник


Ответы (2)


Раздел ensure выполняется при выходе из оператора begin (любым способом), но когда вы retry, вы просто перемещаетесь внутри оператора, поэтому раздел обеспечения не будет выполнен.

Попробуйте эту версию вашего примера, чтобы лучше понять, что происходит:

attempts = 0
begin
  make_service_call()
rescue Exception
  attempts += 1
  retry unless attempts > 2
  exit -1
ensure
  puts "ensure! #{attempts}"
end
person mu is too short    schedule 03.06.2011

ensure выполняется один раз, непосредственно перед выходом из блока кода, и он будет вызываться в это время.

Но из-за условия unless attempts>2 и того факта, что ensure будет вызываться только «непосредственно перед выходом кода» (например, из-за exit -1), attempts += 1 не будет выполняться, и поэтому возникает бесконечный цикл.

ensure похож на __finally в C++: вы можете catch исключить исключение, а затем использовать goto : но finally не будет вызываться до тех пор, пока функция фактически не выйдет.

person Zabba    schedule 03.06.2011