[英]Is #{} always equivalent to to_s?
In " What does it mean to use the name of a class for string interpolation? ", Candide suggested that #{}
inside a string implicitly calls to_s
. 在“ 使用类的名称进行字符串插值是什么意思? ”,Candide建议字符串中的#{}
隐式调用to_s
。 So, for instance: 所以,例如:
my_array = [1, 2, 3, 4]
p my_array.to_s # => "[1, 2, 3, 4]"
p "#{my_array}" # => "[1, 2, 3, 4]"
However, if to_s
for Array is redefined as shown below, I would get different results: 但是,如果重新定义了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>"
I suppose this happens any time and anyhow to_s
is overridden. 我想这会在任何时候发生,无论如何to_s
被覆盖。
What am I supposed to do to keep the equality between to_s
and the expression #{}
in a string, if possible? 如果可能的话,我应该做些什么来保持字符串中的to_s
和表达式#{}
之间的相等性?
I came across this issue in a RubyMonk lesson : what according to the lesson # {ogres}
should return, according to my experience is something different. 我在RubyMonk课程中遇到了这个问题:根据我的经验,根据课程# {ogres}
应该返回的是不同的东西。
Please take a look at the documentation for Object#to_s
. 请查看Object#to_s
的文档。 It says that to_s
should return a String
. 它说to_s
应该返回一个String
。 When you override a method, you should always honor its contract. 覆盖方法时,应始终遵守其合同。 Take a look at the documentatio for Array#to_s
As you can see, it also returns a String
. 看看在documentatio的Array#to_s
正如你所看到的,它也返回一个String
。 [Note that this is true for all the to_X
and the to_XYZ
methods: they must always return an object of the corresponding class and they must not raise
an Exception
or otherwise fail.] [请注意,对于所有 to_X
和to_XYZ
方法都是如此:它们必须 始终返回相应类的对象,并且它们不得 raise
Exception
或以其他方式失败。
Your implementation of to_s
, however, does not return a String
. 但是,您的to_s
实现不会返回String
。 It returns an Array
, thus violating to_s
's contract. 它返回一个Array
,因此违反了 to_s
的合同。 Once you violate a method's contract, all bets are off. 一旦违反方法的合同,所有投注都将被取消。 Personally, I think it would be more appropriate to raise
a TypeError
exception here, but Ruby is trying to be nice and returns some String
instead, which (in this case) prints the class name and some unique identifier. 就个人而言,我认为在这里raise
一个TypeError
异常会更合适,但是Ruby试图变得更好并且返回一些 String
,而在这种情况下,它会打印出类名和一些唯一标识符。
Here is the commit to the RubySpec project which (implicitly) states that no Exception
is raise
d and explicitly states that an implementation-defined but otherwise unspecified String
is returned: The spec for interpolation when Object#to_s
did not return a String
was confusing the default representation of an arbitrary object and Object#inspect
. 这是对RubySpec项目的提交,它(隐式地)声明没有raise
Exception
并且明确声明返回实现定义但未指定的String
: 当Object#to_s
没有返回String
时插值的规范令人困惑任意对象的默认表示和Object#inspect
。
The latest version of the spec, before the project was closed looks like this language/string_spec.rb#L197-L208
: 在项目关闭之前,规范的最新版本看起来像这个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
As you can see, all that is guaranteed in this case, is that you won't get an Exception
, and that you will get a String
, however, it says nothing about what that String
looks like. 正如您所看到的,在这种情况下保证的是,您将不会获得Exception
,并且您将获得一个String
,但是,它没有说明String
外观。
Look at what Ruby is telling you: 看看Ruby告诉你的内容:
"#{my_array}" # => "#<Array:0x007f74924c2bc0>"
That means Ruby is seeing an array returned by your to_s
method, not a string as Ruby expects and like it'd see if you hadn't overridden the original Array.to_s
. 这意味着Ruby会看到你的to_s
方法返回一个数组,而不是像Ruby期望的那样的字符串,就像你看到你是否没有覆盖原始的Array.to_s
。
Instead use something like: 而是使用类似的东西:
'[%s]' % self.map { |elem| elem.to_s }.join(', ')
Change your code to return a String and you'll be okay. 更改你的代码以返回一个字符串,你会没事的。
Consider this: 考虑一下:
[].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]"
In general practice, I'd recommend being cautious overriding core and STD-Lib classes' to_s
as they're doing what they should. 在一般实践中,我建议谨慎地覆盖核心和STD-Lib类的to_s
因为它们正在做它们应该做的事情。 For custom classes it's a good idea to implement to_s
mimicking the same output as the core classes. 对于自定义类,实现to_s
模仿与核心类相同的输出是个好主意。 Occasionally we'll have to get fancy and offer more detailed views into what an instance of an object looks like, but that's when we override inspect
. 偶尔我们必须得到幻想,并提供更详细的视图来查看对象的实例是什么样的,但是当我们覆盖inspect
。
Your definition of Array#to_s
returns an array, on which to_s
would need to be further applied. 您对Array#to_s
定义返回一个数组,在该数组上需要进一步应用to_s
。 This would result in infinite recursion. 这将导致无限递归。 I suspect Ruby has internal implementation to cut off such infinite recursion in case of "#{my_array}"
. 我怀疑Ruby有内部实现,以便在"#{my_array}"
情况下切断这种无限递归。 For p my_array.to_s
, my_array.to_s
is an array, and p
applies Array#inspect
, which does not result in infinite recursion. 对于p my_array.to_s
, my_array.to_s
是一个数组, p
应用Array#inspect
,这不会导致无限递归。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.