[英]Is #{} always equivalent to to_s?
在“ 使用類的名稱進行字符串插值是什么意思? ”,Candide建議字符串中的#{}
隱式調用to_s
。 所以,例如:
my_array = [1, 2, 3, 4]
p my_array.to_s # => "[1, 2, 3, 4]"
p "#{my_array}" # => "[1, 2, 3, 4]"
但是,如果重新定義了to_s
for Array,如下所示,我會得到不同的結果:
class Array
def to_s
self.map { |elem| elem.to_s }
end
end
p my_array.to_s # => ["1", "2", "3", "4"]
p "#{my_array}" # => "#<Array:0x007f74924c2bc0>"
我想這會在任何時候發生,無論如何to_s
被覆蓋。
如果可能的話,我應該做些什么來保持字符串中的to_s
和表達式#{}
之間的相等性?
我在RubyMonk課程中遇到了這個問題:根據我的經驗,根據課程# {ogres}
應該返回的是不同的東西。
請查看Object#to_s
的文檔。 它說to_s
應該返回一個String
。 覆蓋方法時,應始終遵守其合同。 看看在documentatio的Array#to_s
正如你所看到的,它也返回一個String
。 [請注意,對於所有 to_X
和to_XYZ
方法都是如此:它們必須 始終返回相應類的對象,並且它們不得 raise
Exception
或以其他方式失敗。
但是,您的to_s
實現不會返回String
。 它返回一個Array
,因此違反了 to_s
的合同。 一旦違反方法的合同,所有投注都將被取消。 就個人而言,我認為在這里raise
一個TypeError
異常會更合適,但是Ruby試圖變得更好並且返回一些 String
,而在這種情況下,它會打印出類名和一些唯一標識符。
這是對RubySpec項目的提交,它(隱式地)聲明沒有raise
Exception
並且明確聲明返回實現定義但未指定的String
: 當Object#to_s
沒有返回String
時插值的規范令人困惑任意對象的默認表示和Object#inspect
。
在項目關閉之前,規范的最新版本看起來像這個language/string_spec.rb#L197-L208
:
it "uses an internal representation when #to_s doesn't return a String" do obj = mock('to_s') obj.stub!(:to_s).and_return(42) # See rubyspec commit 787c132d by yugui. There is value in # ensuring that this behavior works. So rather than removing # this spec completely, the only thing that can be asserted # is that if you interpolate an object that fails to return # a String, you will still get a String and not raise an # exception. "#{obj}".should be_an_instance_of(String) end
正如您所看到的,在這種情況下保證的是,您將不會獲得Exception
,並且您將獲得一個String
,但是,它沒有說明String
外觀。
看看Ruby告訴你的內容:
"#{my_array}" # => "#<Array:0x007f74924c2bc0>"
這意味着Ruby會看到你的to_s
方法返回一個數組,而不是像Ruby期望的那樣的字符串,就像你看到你是否沒有覆蓋原始的Array.to_s
。
而是使用類似的東西:
'[%s]' % self.map { |elem| elem.to_s }.join(', ')
更改你的代碼以返回一個字符串,你會沒事的。
考慮一下:
[].class # => Array
[].to_s.class # => String
class Array
def to_s
self.map { |elem| elem.to_s }
end
end
[].to_s.class # => Array
class Array
def to_s
'[%s]' % self.map { |elem| elem.to_s }.join(', ')
end
end
[].to_s.class # => String
my_array = [1, 2, 3, 4]
"#{my_array}" # => "[1, 2, 3, 4]"
在一般實踐中,我建議謹慎地覆蓋核心和STD-Lib類的to_s
因為它們正在做它們應該做的事情。 對於自定義類,實現to_s
模仿與核心類相同的輸出是個好主意。 偶爾我們必須得到幻想,並提供更詳細的視圖來查看對象的實例是什么樣的,但是當我們覆蓋inspect
。
您對Array#to_s
定義返回一個數組,在該數組上需要進一步應用to_s
。 這將導致無限遞歸。 我懷疑Ruby有內部實現,以便在"#{my_array}"
情況下切斷這種無限遞歸。 對於p my_array.to_s
, my_array.to_s
是一個數組, p
應用Array#inspect
,這不會導致無限遞歸。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.