From: Daniel Brockman Date: 2005-07-07T01:25:37+09:00 Subject: Re: Inheriting Array and slice() behaviour Sylvain Joyeux writes: > I have a class inheriting Array, Hi Sylvain, I'm sorry, but this is asking for trouble. > and I expected slice() and [] to return Array objects. I think I'd expect them to return instances of the derived class. Surely a subrange of a Foo is also a Foo? > Unfortunately > > irb(main):001:0> class Expression < Array > irb(main):002:1> end > => nil > irb(main):003:0> test = Expression.new > => [] > irb(main):004:0> test.concat( (1..10).to_a ) > => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] > irb(main):005:0> test.slice(1..5) > => [2, 3, 4, 5, 6] > irb(main):006:0> test.slice(1..5).class > => Expression > > And I don't understand why ... Looking through array.c, it's pretty easy to see why this happens on a purely mechanical level --- I'll walk you through it. We'll start in `Init_Array': rb_define_method(rb_cArray, "[]", rb_ary_aref, -1); rb_define_method(rb_cArray, "slice", rb_ary_aref, -1); If we call this method with two arguments, the following will happen: VALUE rb_ary_aref(argc, argv, ary) int argc; VALUE *argv; VALUE ary; { VALUE arg; long beg, len; if (argc == 2) { [...] beg = NUM2LONG(argv[0]); len = NUM2LONG(argv[1]); [...] return rb_ary_subseq(ary, beg, len); } [...] } static VALUE rb_ary_subseq(ary, beg, len) VALUE ary; long beg, len; { VALUE klass, ary2, shared; [...] klass = rb_obj_class(ary); [...] ary2 = ary_alloc(klass); [...] return ary2; } static VALUE ary_alloc(klass) VALUE klass; { NEWOBJ(ary, struct RArray); OBJSETUP(ary, klass, T_ARRAY); [...] return (VALUE)ary; } #define OBJSETUP(obj,c,t) do {\ RBASIC(obj)->flags = (t);\ RBASIC(obj)->klass = (c);\ if (rb_safe_level() >= 3) FL_SET(obj, FL_TAINT);\ } while (0) However, I'm ignorant about the rationale of this behavior. > Moreover, since my Expression class has instance variables, the > instance produced by slice() does not have it anymore (which is bad) That is bad. But I'm not sure whether it could be easily fixed; arrays are pretty special in Ruby. For example, when you say foo[1..5], a new array is returned, but no elements are copied. > I wokarounded this by using Delegator > Any idea on why this behaviour ? Although I agree that it would be nice if inheriting from Array worked painlessly, that is unfortunately not (currently) the case. If you are looking for practical advice, I say avoid inheriting from core classes like Array, and stick with Delegator. On the other hand, if you are seeking to discuss the possibilities of changing how Array is implemented, don't let me stand in your way. -- Daniel Brockman So really, we all have to ask ourselves: Am I waiting for RMS to do this? --TTN.