Исключения Ruby - почему «еще»? - PullRequest
48 голосов
/ 08 июня 2011

Я пытаюсь понять исключения в Ruby, но я немного запутался.В учебнике, который я использую, говорится, что если возникает исключение, которое не соответствует ни одному из исключений, указанных в выражениях спасения, вы можете использовать «else», чтобы перехватить его:

begin  
# -  
rescue OneTypeOfException  
# -  
rescue AnotherTypeOfException  
# -  
else  
# Other exceptions
ensure
# Always will be executed
end

Однако я такжепозже в уроке «спасение» использовалось без указания указанного исключения:

begin
    file = open("/unexistant_file")
    if file
         puts "File opened successfully"
    end
rescue
    file = STDIN
end
print file, "==", STDIN, "\n"

Если вы можете сделать это, то мне когда-нибудь понадобится , чтобы использовать другое?Или я могу просто использовать общее спасение в конце, как это?

begin  
# -  
rescue OneTypeOfException  
# -  
rescue AnotherTypeOfException  
# -  
rescue
# Other exceptions
ensure
# Always will be executed
end

Ответы [ 5 ]

90 голосов
/ 08 июня 2011

else предназначен для завершения блока без исключения.ensure запускается независимо от того, успешно завершен блок или нет.Пример:

begin
  puts "Hello, world!"
rescue
  puts "rescue"
else
  puts "else"
ensure
  puts "ensure"
end

Будет напечатано Hello, world!, затем else, затем ensure.

5 голосов
/ 15 марта 2016

Вот конкретный вариант использования else в выражении begin. Предположим, вы пишете автоматизированные тесты и хотите написать метод, который возвращает ошибку, вызванную блоком. Но вы также хотите, чтобы тест не прошел, если блок не вызывает ошибку. Вы можете сделать это:

def get_error_from(&block)
  begin
    block.call
  rescue => err
    err  # we want to return this
  else
    raise "No error was raised"
  end
end

Обратите внимание, что вы не можете переместить raise внутри блока begin, потому что он получит rescue d. Конечно, есть и другие способы без использования else, например, проверка, является ли err nil после end, но это не так кратко.

Лично я редко использую else таким образом, потому что я думаю, что он редко нужен, но в таких редких случаях он пригодится.

EDIT

Другой случай использования произошел со мной. Вот типичный begin / rescue:

begin
  do_something_that_may_raise_argument_error
  do_something_else_when_the_previous_line_doesnt_raise
rescue ArgumentError => e
  handle_the_error
end

Почему это не идеально? Потому что намерением является rescue, когда do_something_that_may_raise_argument_error поднимает ArgumentError, не , когда do_something_else_when_the_previous_line_doesnt_raise поднимает.

Обычно лучше использовать begin / rescue для переноса минимального кода, который вы хотите защитить от raise, потому что в противном случае:

  • вы можете маскировать ошибки в коде, который не должен был raise
  • намерение rescue труднее расшифровать. Кто-то (включая вашу будущую личность) может прочитать код и спросить: «Какое выражение я хотел защитить? Это выглядит как выражение ABC ... но, возможно, выражение DEF тоже ????» Что задумал автор ?!» Рефакторинг становится намного сложнее.

Вы избежите этих проблем с этим простым изменением:

begin
  do_something_that_may_raise_argument_error
rescue ArgumentError => e
  handle_the_error
else
  do_something_else_when_the_previous_line_doesnt_raise
end
1 голос
/ 08 июня 2011

Блок else в конечном блоке начала восстановления используется, когда вы, вероятно, ожидаете, что произойдет какое-то исключение. Если вы выполняете все ожидаемые исключения, но по-прежнему ничего не вызываете, то в своем блоке else вы можете делать все, что вам нужно, теперь, когда вы знаете, что ваш исходный код работает без ошибок.

0 голосов
/ 06 декабря 2017

Благодаря else иногда можно объединить два вложенных begin end блока.
Итак (упрощенный пример из моего текущего кода) вместо:

  begin
    html = begin
      NetHTTPUtils.request_data url
    rescue NetHTTPUtils::Error => e
      raise unless 503 == e.code
      sleep 60
      retry
    end
    redo unless html["market"]
  end

Вы пишете:

  begin
    html = NetHTTPUtils.request_data url
  rescue NetHTTPUtils::Error => e
    raise unless 503 == e.code
    sleep 60
    retry
  else
    redo unless html["market"]
  end
0 голосов
/ 06 марта 2017

Единственная причина, которую я вижу для блока else, заключается в том, что вы хотите выполнить что-то перед блоком ensure, когда код в блоке begin не вызывает ошибок.

begin
  puts "Hello"
rescue
  puts "Error"
else
  puts "Success"
ensure
  puts "my old friend"
  puts "I've come to talk with you again."
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...