(sort of) casting types in Ruby: changing class of an instance I defined the class Rectangle:

class Rectangle
  attr_reader :b, :h
  def initialize(b, h)
    @b = b
    @h = h
  end
  def area
    @b*@h
  end
  def to_s
    "Rectangle #{@b}x{@h}"
  end
end


and its subclass Square:

class Square &lt; Rectangle

  attr_reader :s
  def initialize(s)
    @s = s
    super(@s, @s)
  end
  def to_s
    "Square of side #{@s}"
  end
end


Now, let's say I defined a Rectangle object r = Rectangle.new(5, 5), but actually it should be a Square because I'd like r.to_s to return "Square of side 5".

I can define a to_square method in the Rectangle class that returns a Square object equivalent to r, but, is it possible to write a to_square! method that would actually change the r class to Square without returning another object, so that r.class would now return Square instead of Rectangle?

And, what if I'd like:

Square(r)


to return the Square equivalent object of r, just like:

Integer("3")


which returns the Integer 3?

Is that possible? And, if so, how?
First off, why bother with a separate Square class?

Personally, I'd make a Rectangle class' to_square! method simply set both width and height to Math.sqrt(area). The result is still a Rectangle, just a square one.

The Rectangle's initializer method could also be written with an optional second argument; If there's only one argument, use that for both width and height, and you have a square.

Furthermore, I'd add a square? method. Checking obj.square? is, in my view, preferable to checking obj.class == Square. You could also use square? in to_s to return one string or the other.

To answer your question, though, I don't know of any way to just change an instance's class. You could however experiment with including different modules depending on whether the rectangle in question is a square or not. But that all seems pretty complex.

Finally, if you want a Square method, simply define one:

def Square(side)
  Rectangle.new(side, side)
end


Even if you have a Square class already, you can still have a method of the same name:

class Square
  # ...
end

def Square(side) # no naming conflict
  Square.new(side)
end