На первый взгляд, методы to_s и to_str могут вызвать недоумение. Ведь оба преобразуют
объект в строковое представление, так?
Но есть и различия. Во-первых, любой объект в принципе можно как-то преобразовать
в строку, поэтому почти все системные классы обладают методом to_s.
Однако метод to_str в системных классах не реализуется никогда.
Как правило, метод to_str применяется для объектов, очень похожих на строки,
способных «замаскироваться» под строку. В общем, можете считать, что метод
to_s - это явное преобразование, а метод to_str - неявное.
Я уже сказал, что ни в одном системном классе не определен метод to_str (по
крайней мере, мне о таких классах неизвестно). Но иногда они вызывают to_str
(если такой метод существует в соответствующем классе).
Первое, что приходит на ум, - подкласс класса string; но на самом деле объект
любого класса, производного от string, уже является строкой, так что определять
метод to_str излишне.
А вот пример из реальной жизни. Класс Pathname определен для удобства работы
с путями в файловой системе (например, конкатенации). Но путь естественно
отображается на строку (хотя и не наследует классу string).
require 'pathname'
path = Pathname.newC/tmp/myfile")
name = path.to_s # "/tmp/myfile"
name = path.to_str # Vtmp/myfile" {Ну и что?)
# Вот где это оказывается полезно...
heading = "Имя файла равно " + path
puts heading! " Имя файла равно, /tmp/myf.ile''
В этом фрагменте мы просто дописали путь в конец обычной строки " имя
файла равно". Обычно такая операция приводит к ошибке во время выполнения,
поскольку оператор + ожидает, что второй операнд - тоже строка. Но так как в
классе Pathname есть метод to_str, то он вызывается. Класс Pathname «маскируется
» под строку, то есть может быть неявно преобразован в string.
На практике методы to_s и to_str обычно возвращают одно и то же значение,
но это необязательно. Неявное преобразование должно давать «истинное строковое
значение» объекта, а явное можно расценивать как «принудительное» преобразование.
Метод puts обращается к методу to_s объекта, чтобы получить его строковое
представление. Можно считать, что это неявный вызов явного преобразования. То
же самое справедливо в отношении интерполяции строк. Вот пример:
class Helium
def to_s
"He"
end
def to_str
"гелий"
end
end
# Элемент He.
# Элемент гелий.
# Элемент He.
e = Helium.new
print "Элемент "
puts e
puts "Элемент " + e
puts "Элемент #{e}"
Как видите, разумное определение этих методов в собственном классе может
несколько повысить гибкость применения. Но что сказать об идентификации объектов,
переданных методам вашего класса?
Предположим, например, что вы написали метод, который ожидает в качестве
параметра объект 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 нет ничего волшебного. Предполагается, что он возвращает строку, но если вы пишете такой метод сами, ответственность за то, что он действительно так и поступает, ложится на вас.
