Proper comparison of recursive Struct & Range
The following code freezes the latest ruby 1.9:
MyClass = Struct.new(:foo)
x = MyClass.new; x[:foo] = x
y = MyClass.new; y[:foo] = y
x == y # Loops forever (can not be interrupted)
Solution: rb_struct_equal & rb_struct_eql should handle recursion in a similar fashion as Array and Hash (i.e. by calling rb_exec_recursive_paired and returning Qtrue if recursion is detected). I could make a patch if needed.
Searching the source code for rb_exec_recursive revealed that Range is potentially recursive too (see range_inspect). The ==, eql? and === methods do not call rb_exec_recursive_paired and are thus also potentially troublesome. To build a recursive Range is not trivial though; either some intermediate container class is needed or else some crazy thing like:
class CrazyRange < Range
def <=>(x) 0 # Needed so CrazyRange can be the begin and end values of a range... end
CrazyRange.new == CrazyRange.new # Loops forever (can not be interrupted)
I'm not sure that it is worth modifying ==, eql? and === to use rb_exec_recursive_paired and make them bulletproof.
Note that both Struct#hash and Range#hash face the same issue than Array#hash & Hash#hash (see issue #1852)
Updated by marcandre (Marc-Andre Lafortune) over 10 years ago
Here is a patch for the latest 1.9.x that fixes comparison between recursive Struct. The right result is now returned, instead of looping forever. Note: this fixes Struct only; Range has not been patched. Rubyspecs have been updated.