From: "bitsweat (Jeremy Kemper)" <jeremy@...>
Date: 2012-11-15T07:57:07+09:00
Subject: [ruby-core:49349] [ruby-trunk - Bug #7342] String#<=> checks for a #to_str method on other but never uses it?


Issue #7342 has been updated by bitsweat (Jeremy Kemper).


"It should be used internally to retrieve the string representation of an object." That's explicit coercion. Implicit coercion with #to_str means the object acts a string and the method needn't be called.

This method is used for more than its return value. It's in a strange limbo world between Ruby and the C API :)

The presence of #to_str indicates that the object obeys an entire String contract such that the C API can work with the object without making Ruby method calls. You note correctly that providing #to_str but not #<=> prohibits comparison. That's because by omitting #<=> you've already broken the "I am a string" contract.

Check out how time.c for another example of checking #to_str and, more generally, see rb_check_convert_type for many other examples of implicit coercion in practice: to_path, to_int, to_ary, etc.
----------------------------------------
Bug #7342: String#<=> checks for a #to_str method on other but never uses it?
https://bugs.ruby-lang.org/issues/7342#change-32905

Author: jballanc (Joshua Ballanco)
Status: Open
Priority: Normal
Assignee: 
Category: 
Target version: 
ruby -v: 2.0.0


=begin
This isn't exactly a bug, as much as a request for clarification. I was looking at the semantics of the (({<=>})) operator and noticed something curious. For most classes, when evaluating (({thing <=> other})), if (({other})) is not of a compatible type, then (({nil})) is returned.

The exceptions (as far as I can find) are String and Time. For the Time class, if (({other})) is not a kind of (({Time})), then the reverse comparison (({other <=> thing})) is tried and the inverse of this result is returned (if not nil). For String, the reverse comparison is only tried IF (({other.respond_to?(:to_str)})), HOWEVER the referenced (({other.to_str})) method is never called. For example:

    class NotAString
      def <=>(other)
        1
      end
      def to_str
        raise "I'm not a string!"
      end
    end
    
    "test" <=> NotAString.new #=> -1

This seems very counterintuitive to me. I would expect that if my class implemented (({to_str})), that the return value of this would be used for comparison.
=end



-- 
http://bugs.ruby-lang.org/