В этом фрагменте мы просто дописали путь в конец обычной строки 'Имя файла равно'
. Обычно такая операция приводит к ошибке во время выполнения, поскольку оператор +
ожидает, что второй операнд — тоже строка. Но так как в классе Pathname
есть метод to_str
, то он вызывается. Класс Pathname
«маскируется» под строку, то есть может быть неявно преобразован в String
.
На практике методы to_s
и to_str
обычно возвращают одно и то же значение, но это необязательно. Неявное преобразование должно давать «истинное строковое значение» объекта, а явное можно расценивать как «принудительное» преобразование.
Метод puts
обращается к методу to_s
объекта, чтобы получить его строковое представление. Можно считать, что это неявный вызов явного преобразования. То же самое справедливо в отношении интерполяции строк. Вот пример:
class Helium
def to_s
'He'
end
def to_str
'гелий'
end
end
e = Helium.new
print 'Элемент '
puts e # Элемент He.
puts 'Элемент ' + e # Элемент гелий.
puts 'Элемент #{e}' # Элемент He.
Как видите, разумное определение этих методов в собственном классе может несколько повысить гибкость применения. Но что сказать об идентификации объектов, переданных методам вашего класса?
Предположим, например, что вы написали метод, который ожидает в качестве параметра объект String
. Вопреки философии «утипизации», так делают часто, и это вполне оправдано. Например, предполагается, что первый параметр метода File.new
— строка.
Решить эту проблему просто. Если вы ожидаете на входе строку, проверьте, имеет ли объект метод to_str
, и при необходимости вызывайте его.
def set_title(title)
if title.respond_to? :to_str
title = title.to_str
end
# ...
end
Ну а если объект не отвечает на вызов метода to_str
? Есть несколько вариантов действий. Можно принудительно вызвать метод to_s
; можно проверить, принадлежит ли объект классу String
или его подклассу; можно, наконец, продолжать работать, понимая, что при попытке выполнить операцию, которую объект не поддерживает, мы получим исключение ArgumentError
.
Короткий путь к цели выглядит так:
title = title.to_str rescue title
Он опирается на тот факт, что при отсутствии реализации метода to_str
возникнет исключение. Разумеется, модификаторы rescue
могут быть вложенными:
title = title.to_str rescue title.to_s rescue title
# Обрабатывается маловероятный случай, когда отсутствует даже метод to_s.
С помощью неявного преобразования можно было бы сделать строки и числа практически эквивалентными:
class Fixnum
def to_str
self.to_s end
end
str = 'Число равно ' + 345 # Число равно 345.
Но я не рекомендую так поступать: «много хорошо тоже нехорошо». В Ruby, как и в большинстве языков, строки и числа — разные сущности. Мне кажется, что ясности ради преобразования, как правило, должны быть явными.
И еще: в методе to_str
нет ничего волшебного. Предполагается, что он возвращает строку, но если вы пишете такой метод сами, ответственность за то, что он действительно так и поступает, ложится на вас.
2.17. Дописывание в конец строки
Для конкатенации строк применяется оператор <<
. Он «каскадный», то есть позволяет выполнять подряд несколько операций над одним и тем же операндом-приемником.
str = 'А'
str << [1,2,3].to_s << ' ' << (3.14).to_s
# str теперь равно 'А123 3.14'.
Если число типа Fixnum
принадлежит диапазону 0..255, то оно будет преобразовано в символ:
str = 'Marlow'
str << 101 << ', Christopher'
# str теперь равно 'Marlowe, Christopher'.
2.18. Удаление хвостовых символов новой строки и прочих
Часто бывает необходимо удалить лишние символы в конце строки. Типичный пример — удаление символа новой строки после чтения строки из внешнего источника.
Метод chop
удаляет последний символ строки (обычно это символ новой строки). Если перед символом новой строки находится символ перевода каретки (
), он тоже удаляется. Причина такого поведения заключается в том, что разные операционные системы неодинаково трактуют понятие «новой строки». В UNIX-подобных системах новая строка представляется символом
. А в DOS и Windows для этой цели используется пара символов
.
str = gets.chop # Прочитать, удалить символ новой строки.
s2 = 'Some string
' # 'Some string' (нет символа новой строки).
s3 = s2.chop! # s2 теперь тоже равно 'Some string'.
s4 = 'Other string
'
s4.chop! # 'Other string' (нет символа новой строки).
Обратите внимание, что при вызове варианта chop!
операнд-источник модифицируется.
Важно еще отметить, что последний символ удаляется, даже если это не символ новой строки:
str = 'abcxyz'