end
Здесь в переменной err
хранится объект-исключение; при выводе ее на печать объект будет преобразован в осмысленную символьную строку. Отметим, что коль скоро тип ошибки не указан, то этот обработчик rescue
будет перехватывать все исключения, производные от класса StandardError
. В конструкции rescue => variable
можно перед символом =>
дополнительно указать тип ошибки.
Если типы ошибок указаны, то может случиться так, что тип реально возникшего исключения не совпадает ни с одним из них. На этот случай после всех обработчиков rescue
разрешается поместить ветвь else
.
begin
# Код, в котором может возникнуть ошибка...
rescue Type1
# ...
rescue Type2
# ...
else
#Прочие исключения...
end
Часто мы хотим каким-то образом восстановиться после ошибки. В этом поможет ключевое слово retry
(внутри тела обработчика rescue
). Оно позволяет повторно войти в блок begin
и попытаться еще раз выполнить операцию:
begin
# Код, в котором может возникнуть ошибка...
rescue
# Пытаемся восстановиться...
retry # Попробуем еще раз.
end
Наконец, иногда необходим код, который «подчищает» что-то после выполнения блока begin-end
. В этом случае можно добавить часть ensure
:
begin
# Код, в котором может возникнуть ошибка...
rescue
# Обработка исключений.
ensure
# Этот код выполняется в любом случае.
end
Код, помещенный внутрь части ensure
, выполняется при любом способе выхода из блока begin-end
— вне зависимости от того, произошло исключение или нет.
Исключения можно перехватывать еще двумя способами. Во-первых, существует форма rescue
в виде модификатора:
x = a/b rescue puts('Деление на нуль!')
Кроме того, тело определения метода представляет собой неявный блок begin-end
; слово begin
опущено, а все тело метода подготовлено к обработке исключения и завершается словом end
:
def some_method
# Код...
rescue
# Восстановление после ошибки...
end
На этом мы завершаем как обсуждение обработки исключений, так и рассмотрение основ синтаксиса и семантики в целом.
У Ruby есть многочисленные аспекты, которых мы не коснулись. Оставшаяся часть главы посвящена более развитым возможностям языка, в том числе рассмотрению ряда практических приемов, которые помогут программисту среднего уровня научиться «думать на Ruby».
1.3. ООП в Ruby
В языке Ruby есть все элементы, которые принято ассоциировать с объектно-ориентированными языками: объекты с инкапсуляцией и сокрытием данных, методы с полиморфизмом и переопределением, классы с иерархией и наследованием. Но Ruby идет дальше, добавляя ограниченные возможности создания метаклассов, синглетные методы, модули и классы-примеси.
Похожие идеи, только под иными именами, встречаются и в других объектно-ориентированных языках, но одно и то же название может скрывать тонкие различия. В этом разделе мы уточним, что в Ruby понимается под каждым из элементов ООП.
1.3.1. Объекты
В Ruby все числа, строки, массивы, регулярные выражения и многие другие сущности фактически являются объектами. Работа программы состоит в вызове методов разных объектов:
3.succ # 4
'abc'.upcase # 'ABC'
[2,1,5,3,4].sort # [1,2,3,4,5]
someObject.someMethod # какой-то результат
В Ruby каждый объект представляет собой экземпляр какого-то класса. Класс содержит реализацию методов:
'abc'.class # String
'abc'.class.class # Class
Помимо инкапсуляции собственных атрибутов и операций объект в Ruby имеет уникальный идентификатор:
'abc'.object_id # 53744407
Этот идентификатор объекта обычно не представляет интереса для программиста.
1.3.2. Встроенные классы
Свыше 30 классов уже встроено в Ruby. Как и во многих других объектно-ориентированных языках, в нем не допускается множественное наследование, но это еще не означает, что язык стал менее выразительным. Современные языки часто построены согласно модели одиночного наследования. Ruby поддерживает модули и классы-примеси, которые мы обсудим в следующей главе. Также реализованы идентификаторы объектов, что позволяет строить устойчивые, распределенные и перемещаемые объекты.
Для создания объекта существующего класса обычно используется метод new
: