簡體   English   中英

#{}總是等同於to_s嗎?

[英]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_Xto_XYZ方法都是如此:它們必須 始終返回相應類的對象,並且它們不得 raise Exception或以其他方式失敗。

但是,您的to_s實現不會返回String 它返回一個Array ,因此違反了 to_s的合同。 一旦違反方法的合同,所有投注都將被取消。 就個人而言,我認為在這里raise一個TypeError異常會更合適,但是Ruby試圖變得更好並且返回一些 String ,而在這種情況下,它會打印出類名和一些唯一標識符。

這是對RubySpec項目的提交,它(隱式地)聲明沒有raise Exception並且明確聲明返回實現定義但未指定的StringObject#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_smy_array.to_s是一個數組, p應用Array#inspect ,這不會導致無限遞歸。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM