To_s против to_str (и to_i/to_a/to_h против to_int/to_ary/to_hash) в Ruby
Я изучаю Ruby, и я видел несколько методов, которые меня немного смущают, особенно to_s
vs to_str
(и аналогичным образом to_i
/to_int
, to_a
/to_ary
и to_h
/to_hash
). То, что я прочитал, объясняет, что более короткая форма (например, to_s
) предназначена для явных преобразований, а более длинная - для неявных преобразований.
Я действительно не понимаю, как использовать to_str
. Может ли что-то отличное от String определять to_str
? Можете ли вы дать практическое применение для этого метода?
Ответы
Ответ 1
Обратите внимание, что все это относится к каждой паре "коротких" (например, to_s
/to_i
/to_a
/to_h
) по сравнению с "длинной" (например, to_str
/to_int
/to_ary
/to_hash
) в Ruby (для их соответствующих типов), поскольку все они имеют одинаковую семантику.
У них разные значения. Вы не должны реализовывать to_str
, если ваш объект не работает как строка, а не просто представляется в виде строки. Единственным основным классом, который реализует to_str
, является сама строка.
Из программирования Ruby (цитируется это сообщение в блоге, которое стоит прочитать все):
[to_i
и to_s
] не являются особенно строгими: если у объекта есть какое-то достойное представление в виде строки, например, у него, вероятно, будет метод to_s
... [to_int
и to_str
] являются строгими функциями преобразования: вы реализуете их только в том случае, если [ваш] объект может быть естественным образом использован в каждом месте, где может использоваться строка или целое число.
Документация Older Ruby от Pickaxe позволяет:
В отличие от to_s
, который поддерживается почти всеми классами, to_str
обычно реализуется только теми классами, которые действуют как строки.
Например, помимо Integer, Float и Numeric реализовать to_int
(to_i
эквивалент to_str
), потому что они оба могут легко заменить Integer (все они фактически являются числами). Если ваш класс не имеет тесной связи с String, вы не должны реализовывать to_str
.
Ответ 2
Чтобы понять, следует ли использовать/реализовать to_s
/to_str
, рассмотрим некоторые примеры. Показывается, что , когда этот метод терпит неудачу.
1.to_s # returns "1"
Object.new.to_s # returns "#<Object:0x4932990>"
1.to_str # raises NoMethodError
Object.new.to_str # raises NoMethodError
Как мы видим, to_s
счастлив превратить любой объект в строку. С другой стороны, to_str
вызывает ошибку, когда ее параметр не похож на строку.
Теперь посмотрим на Array#join
.
[1,2].join(',') # returns "1,2"
[1,2].join(3) # fails, the argument does not look like a valid separator.
Полезно, что Array#join
преобразует в строку элементы в массиве (независимо от того, что они есть на самом деле) перед их присоединением, поэтому Array#join
вызывает to_s
на них.
Однако разделитель должен быть строкой - кто-то, вызывающий [1,2].join(3)
, скорее всего, совершит ошибку. Вот почему Array#join
вызывает to_str
в разделителе.
Тот же принцип, по-видимому, сохраняется и для других методов. Рассмотрим to_a
/to_ary
на хеш:
{1,2}.to_a # returns [[1, 2]], an array that describes the hash
{1,2}.to_ary # fails, because a hash is not really an array.
Вкратце, вот как я это вижу:
- вызов
to_s
, чтобы получить строку, описывающую объект.
- call
to_str
, чтобы убедиться, что объект действительно действует как строка.
- реализовать
to_s
, когда вы можете создать строку, описывающую ваш объект.
- реализовать
to_str
, когда ваш объект может полностью вести себя как строка.
Я думаю, что случай, когда вы могли бы реализовать to_str
самостоятельно, может быть, это класс ColoredString
- строка с прикрепленным к ней цветом. Если вам кажется, что передача цветной запятой на join
не является ошибкой и должна привести к "1,2"
(даже если эта строка не будет окрашена), тогда реализуйте to_str
в ColoredString.