to_s 和 to_str 的区别:http://rubylution.herokuapp.com/topics/17 to_s 和 inspect 的区别:http://rubylution.herokuapp.com/topics/19
#4 楼 @jjym 其实这里面有细微差别的。并且这样的差别并不影响初级使用者,但是高阶一点的使用者却可以用的很顺手。
to_s means "You can turn me into a string", while to_str means "For all intents and purposes, I am a string".
to_s 是表示把一个对象的字符串状态呈现出来,不管结果如何。只要得到字符串的状态。 所以普通对象一般 to _s 后都是这样"# < A:0x9c5ce8 >",几乎没有意义。
而一般对象是没有定义 to_str 的,如果定义了就表示这个对象能完美的呈现字符串状态(比如 Path 对象,Rails.env),在与字符串做相关操作(join / gsub/ <<等),就会优先调用 to_str。
ruby-1.9.3-p0 :050 > Rails.env.class
=> ActiveSupport::StringInquirer
ruby-1.9.3-p0 :051 > "hello " << Rails.env
=> "hello development"
简单的说,一个对象定义了 to_str 就表示它 acts_like_string,但这种实现比继承和 mix-in 要轻量很多。
#6 楼 @jjym 定义了 to_str 就像一个对象贴上这样的标签:“我是一个字符串噢!字符串们我们可以一起 OOXX 啊”
而定义了 to_s 就表示“实在没有办法了你也可以把我变成字符串。至于会变成什么样子鬼知道呢!”
ruby-1.9.3-p0 :052 > class XX
ruby-1.9.3-p0 :053?> def to_str
ruby-1.9.3-p0 :054?> "ooxx"
ruby-1.9.3-p0 :055?> end
ruby-1.9.3-p0 :056?> end
=> nil
ruby-1.9.3-p0 :058 > class CC
ruby-1.9.3-p0 :059?> def to_s
ruby-1.9.3-p0 :060?> "to_s"
ruby-1.9.3-p0 :061?> end
ruby-1.9.3-p0 :062?> end
ruby-1.9.3-p0 :064 > "let's go " << XX.new
=> "let's go ooxx"
ruby-1.9.3-p0 :066 > "let's go " << CC.new
TypeError: can't convert CC into String
上面的代码演示了,如果你不定义 to_str,其他 str 根本没办法信任你。
还有一方面就是,to_s to_str inspect 这三个方法的职责不同。 to_str 刚才说了是标识 acts like string inspect 是为了 debug 需要,检查对象内部状态(包括实例变量类变量等) to_s 是一种不负责任的强制转换,每个对象都有的。但一般不需要自己去修改。
想了下,结合 LZ 所说还是有必要这样设计的。 ruby 变量没有类型 所以如果 to_s 代替 to_str,也就是所有对象皆可为字符串 甚至可能让人混淆对象的类型
我习惯需要用 string 的时候先 to_s, 比如 "Hello" + Hooopo.to_s, ["Hello", Hooopo.to_s].join(" ")
to_s 必须显性调用 to_str 类似其他语言的 toString 和__str__, my_obj+str 这类场景自动转换 inspect 类似 python 的 repr
其实 Ruby 中to_?
和 to_???
这类方法是有惯例的。
简单点说: 前者就是让你手动调用的。并且核心类有默认实现,当然,你可以重写默认实现. 后者,不是让你调用的。是系统自动调用的。鸭子类型,就是用类似方法实现的。
#9 楼 @jjym 这篇讲的也不错:http://blog.bigbinary.com/2012/06/26/to_str-in-ruby.html
If an object implements to_str method then it is telling the world that my class might not be String but for all practical purposes treat me like a string. If a class defines to_str then that class is telling the world that altought my class is not String you can treat me like a String.
Ruby could have called to_s method on e and could have produced the result. But ruby refused to do so.
Ruby refused to do so because it found that the object you are trying to add to string is not of type String. When we call to_s we get the string representation of the string. But the object might or might not be behaving like a string.
看了这篇后感觉 to_str 更像是为了避免无意义的字符串而出现的。 Ruby 在设计上很多地方都值得思考,有机会一定要看下 Matz 那本书
今天碰研究一个最佳实践,牵涉到这个这个知识点了,搜到这里,很有收获,也分享下。 ruby 是动态类型的,所以在方法内有时需要对参数类型进行检查,而这种检查最好不用 responds_to?而使用某种显示转换,而像 to_i 这样,所以我需要了解 to_i 和 to_int 的区别。
def need_num num
puts "to_i ---"
num.to_i.times { puts "#{num} times repeated" }
puts "to_int ---"
num.to_int.times { puts "#{num} times repeated" }
puts "times ---"
num.times { puts "#{num} times repeated" }
end
need_num 5
need_num "6"
@hooopo 谢谢分享。但是你的解释不够清晰哈,我上面这个场景,按你在 5 楼的解释,我肯定就是将num
当作number
来对待,那应该调用to_int
,但我测试来看to_i
才符合预期。你不知道用户调用num
时会传进来一个 什么东西,故你不能盼着系统会的自动转换,而要自己显示转换,故应该调用to_i
.
@zw963 @clearJiang 总结的更到位,也更加容易理解,
@hooopo 我了个去,不要对偶等菜鸟失去耐心撒。很感谢,看了例子和引用材料,基本还是弄清楚了的。
我 review 时发现,to_s 要显性调用 这种说法也不精确, "puts invokes to_s method too"
#16 楼 @feitian124 隐式转换是发生在多个对象之间相关操作时候的。比如两个字符串拼接、两个数组连接等。被链接的对象会发生隐式转换。你上面的例子num.to_int
报错是因为字符串就没有 to_ini 方法。和隐式转换没一点关系。