在Ruby中替换to_s方法。 不打印出所需的字符串

所以,我刚刚开始学习Ruby,我在我的类中包含了一个to_s方法,这样我就可以简单地将Object传递给puts方法,让它返回的不仅仅是Object ID。 我犯了一个错误,并将其定义为:

def to_s puts "I'm #{@name} with a health of #{@health}." end 

代替:

 def to_s "I'm #{@name} with a health of #{@health}." end 

所以,当我在使用第一个代码块时执行此操作:

 player1 = Player.new("larry") puts player1 

当我执行上面两行代码而不仅仅是字符串时,我得到一个对象ID和一个字符串。 为什么是这样? 我得到这个输出:

 I'm Larry with a health of 90. # 

我试图考虑为什么程序的第一个版本不只是打印出字符串到控制台,而是返回对象ID和字符串。 我认为当我将对象传递给puts时,所发生的一切就是puts转向并调用to_s方法来获取玩家的字符串表示。 对?

当给定的参数不是字符串或数组时puts调用rb_obj_as_string将其参数转换为字符串(请参阅rb_io_puts )

如果你通过ruby代码库搜索rb_obj_as_string(我发现http://rxr.whitequark.org对此很有用)你可以看到它被定义为

 VALUE rb_obj_as_string(VALUE obj) { VALUE str; if (RB_TYPE_P(obj, T_STRING)) { return obj; } str = rb_funcall(obj, id_to_s, 0); if (!RB_TYPE_P(str, T_STRING)) return rb_any_to_s(obj); if (OBJ_TAINTED(obj)) OBJ_TAINT(str); return str; } 

简而言之:

  • 如果参数已经是一个字符串,则直接返回
  • 调用to_s
  • 如果结果不是字符串,则调用rb_any_to_s并返回该字符串。

rb_any_to_s实现了您正在看到的默认“类名和ID”结果:对于任何对象,它返回一个forms为#的字符串#

回到你的代码,当你运行puts player1它会调用rb_obj_as_string将你的玩家转换成一个字符串。

这首先调用你的to_s方法,它使用puts来输出你的消息。 然后你的方法返回nil(因为这就是总是返回的东西)所以ruby调用rb_any_to_s ,这就是最外面的rb_any_to_s最终使用的东西。

这是因为puts返回nil, to_s那个版本也是to_s

 def to_s puts "I'm #{@name} with a health of #{@health}." end 

使用puts player1player1.to_s方法,打印字符串“我是……”,但返回值是to_s内的puts调用,即nil

因此, player1是一个对象,其中to_s返回nil,因此puts player1放在最后打印inheritance的to_s方法的结果。

Experiential Rule:如果to_s的结果不是String,则ruby返回默认值。

Rule: puts()的应用返回nil,这意味着你的to_s方法返回nil,而nil不是String,所以ruby返回默认值。

另一个例子:

 class Object def inspect 'obj-inspect' end def to_s 'obj-to_s' end end class Dog def inspect 'dog-inspect' end def to_s nil end end puts Dog.new --output:-- # 

一旦to_s无法返回String,ruby就不会沿着方法查找路径继续调用另一个to_s方法。 这有点道理:找到了方法,所以不需要在父类中查找该方法。 ruby也不会调用inspect()来获得结果。

默认来自哪里? 我认为ruby必须直接调用Object#to_s方法,这是一个用C语言编写的方法 – 从而绕过了ruby的方法覆盖机制。

使用puts的第一个示例将写入stdout并返回nil。 它实际上并不返回String。

第二个示例返回一个String。

如果您想要写入控制台,但您还需要返回该值。

 #or put it in a variable first and return that after you print it def to_s puts "I'm #{@name} with a health of #{@health}." "I'm #{@name} with a health of #{@health}." end