При выполнении программы могут возникать непредвиденные проблемы. Файл, который нужно прочитать, может не существовать; диск может оказаться полным в момент записи данных; пользователь может некорректно ввести данные.
ruby> **file = open("some_file")** **ERR: (eval):1:in `open': No such file or directory - some_file**
"Устойчивая" программа должна четко и изящно обработать такую ситуацию. Предусмотреть подобное может быть мучительной, изматывающей задачей. Предполагается, что программисты на С должны проверять результат каждого системного вызова, который потенциально может завершиться неудачей, и немедленно решить что должно быть сделано в этом случае:
FILE *file = fopen("some_file", "r"); if (file == NULL) { fprintf( stderr, "File doesn't exist.\n" ); exit(1); } bytes_read = fread( buf, 1, bytes_desired, file ); if (bytes_read != bytes_desired ) { /* do more error handling here ... */ } ...
Это настолько утомительно, что у программиста может наблюдаться тенденция к росту небрежности; в результате чего программа не обрабатывает надежно исключения. С другой стороны, правильное выполнение работы делает текст программы нецдобочитаемым, поскольку настолько громоздкая обработка ошибок замусоривает значащий код.
В Ruby, как в большинстве современных языков, мы обрабатываем исключения для блоков кода раздельно, таким образом работая эффективно с подобными сюрпризами и не перегружая напрасно программиста или того человека, который позднее будет читать этот код. Блок кода, помеченный
beginдо тех пор, пока не происходит исключительная ситуация, которая передает управление блоку, отвечающему за ее обработку (начинается с
rescue). Если исключения не происходит, то
rescue-блок не выполняется. Следующий метод возвращает первую строку из текстового файла или
nil, если происходит исключение:
def first_line( filename ) begin file = open("some_file") info = file.gets file.close info # Last thing evaluated is the return value rescue nil # Can't read the file? then don't return a string end end
Иногда бывает нужна возможность обойтись с проблемой творчески. Здесь, если требуемый файл недоступен, мы пробуем использовать стандартный поток ввода:
begin file = open("some_file") rescue file = STDIN end begin # ... process the input ... rescue # ... and deal with any other exceptions here. end
retryможет быть использован внутри
rescueдля запуска повторного выполнения блока с
begin. Теперь можно переписать предыдущий пример немного компактнее:
fname = "some_file" begin file = open(fname) # ... process the input ... rescue fname = "STDIN" retry end
Тем не менее, здесь есть недостаток. Несуществующий файл может заставить этот код выполняться снова и снова. Вы должны остерегаться подобной западни при использовании
retryдля обработки исключительных ситуаций.
Любая библиотека Ruby возбуждает исключения при возникновении любой ошибки, и вы также можете явно возбуждать ошибки в своем коде. Для этого используйте
raise. Этот оператор принимает один аргумент, который представляет из себя строку, описывающую исключение. Аргумент не обязателен, но опускать его не стоит. Его можно получить позже из специальной глобальной переменной
$!.
ruby> **raise "test error"** **test error** ruby> **begin** |**raise "test2"** | **rescue** |**print "An error occurred: ",$!, "\n"** | **end** An error occurred: test2 **nil**