Skip to content

Instantly share code, notes, and snippets.

@steveklabnik
Last active December 20, 2015 04:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save steveklabnik/6071687 to your computer and use it in GitHub Desktop.
Save steveklabnik/6071687 to your computer and use it in GitHub Desktop.
Don't inherit from core classes! They will ruin your day! They're implemented in C for speed, and cheat.
irb(main):001:0> class MyArray < Array
irb(main):002:1> def foo
irb(main):003:2> "foo"
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> a1 = MyArray.new
=> []
irb(main):007:0> a1.foo
=> "foo"
irb(main):008:0> (a1 + []).foo
NoMethodError: undefined method `foo' for []:Array
from (irb):8
from /opt/rubies/ruby-2.0.0-p195/bin/irb:12:in `<main>'
irb(main):007:0> MyArray.new.to_a.foo
NoMethodError: undefined method `foo' for []:Array
from (irb):7
from /opt/rubies/ruby-2.0.0-p195/bin/irb:12:in `<main>'
@zspencer
Copy link

What about OStruct?

I tend to wrap hashes/arrays in a containing class then delegate; but when I'm lazy I inherit :X

@inossidabile
Copy link

How is this connected to core classes at all? Totally the same can happen with pure Ruby.

@davidcelis
Copy link

Like others said, unless you'd define your own MyArray#+ operator to coerce the result into a MyArray instead of Array, of course it's gonna ruin your day. This is behavior that is easily avoidable, and I don't think it's a good reason to advise everybody away from inheriting from core classes.

@steveklabnik
Copy link
Author

@garybernhardt
Copy link

This behavior is easily avoidable when implementing the core types, or any types, although it may have performance implications for naive or non-JITed implementations. Array#+ could instantiate self.class instead of hard-coding an Array. Here's a version of Array#+ that doesn't have this problem:

class Array
  def +(other)
    result = self.class.new
    result.replace(self)
    result.concat(other)
    result
  end
end

That makes Steve's examples work:

> class MyArray < Array; def foo; "foo"; end; end
 => nil
> (MyArray.new + []).foo
 => "foo"

It clearly is connected in the core classes, in that they were implemented without considering reuse in this way. Of course, this raises an awkward question: what if self and other are both subclasses of Array, but not the same subclass? Do you return the self type, or the other type? Returning the base type (Array) makes some level of sense in that case, but in practice this comes up much less than the simple "I want to subclass Array and get reasonable results" case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment