Replacing the to_s method in Ruby. Not printing out desired string

后端 未结 4 1107
甜味超标
甜味超标 2020-12-11 12:05

So, I am just beginning to learn Ruby and I included a to_s method in my Class so that I can simply pass the Object to a puts method and have it return more than just the Ob

相关标签:
4条回答
  • 2020-12-11 12:12

    That's because the puts returns nil, so does that version of to_s:

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

    With puts player1, player1.to_s method is called, which prints the String "I'm ...", but the return value is that of the puts call inside to_s, which is nil.

    So player1 is an object of which to_s returns nil, thus puts player1 in the end prints the result of the inherited to_s method.

    0 讨论(0)
  • 2020-12-11 12:29

    When given arguments that are not strings or arrays puts calls rb_obj_as_string to turn its arguments into strings (see rb_io_puts)

    If you search for rb_obj_as_string through the ruby codebase (I find http://rxr.whitequark.org useful for this) you can see it's defined as

    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;
    }
    

    In brief this:

    • returns straightaway if the argument is already a string
    • calls to_s
    • if the result is not a string, call rb_any_to_s and return that.

    rb_any_to_s is what implements the default "class name and id" result that you're seeing: for any object it returns a string of the form #<ClassName: 0x1234567890abcdef>

    Returning to your code, when you run puts player1 it calls rb_obj_as_string to convert your player to a string.

    This first calls your to_s method, which uses puts to output your message. Your method then returns nil (because that's what puts always returns) so ruby calls rb_any_to_s, and that is what the outermost puts ends up using.

    0 讨论(0)
  • 2020-12-11 12:32

    Experiential Rule: If the result of to_s is not a String, then ruby returns the default.

    Application of Rule: puts() returns nil, which means your to_s method returns nil, and nil is not a String, so ruby returns the default.

    Another example:

    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:--
    #<Dog:0x1001b6218>
    

    Once to_s fails to return a String, ruby does not continue along the method lookup path to call another to_s method. That makes some sense: the method was found, so there is no need to look up the method in a parent class. Nor does ruby alternatively call inspect() to get a result.

    Where does the default come from? I think ruby must directly call the Object#to_s method which is a method written in C--thereby bypassing ruby's method overriding mechanism.

    0 讨论(0)
  • 2020-12-11 12:35

    The first example using puts will write to stdout and return nil. It does not actually return a String.

    The second example returns a String.

    If you want to write to the console you can, but you will need to also return the value.

    #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
    
    0 讨论(0)
提交回复
热议问题