From: ruby-core@... Date: 2015-12-16T03:23:19+00:00 Subject: [ruby-core:72169] [Ruby trunk - Feature #11708] Specify a way to override Struct-subclass constructor Issue #11708 has been updated by Marc-Andre Lafortune. It's true that there is no test (and more surprisingly no Rubyspec) on this. I'm not Matz either, but I feel there's no way that this behavior will ever change. First because there's no reason why it would, but more importantly because it would be a source of incompatibility and frustration. I feel you can deduce that `initialize` is overridable from the current doc, as it states that `Struct.new do ... end` is preferred to subclassing. If `Struct.new` was defining an `initialize` (and thus preventing easy overriding of initialize), then there would be a caveat since subclassing would not have this problem. I added a test and hope this will resolve conclusively this issue. ---------------------------------------- Feature #11708: Specify a way to override Struct-subclass constructor https://bugs.ruby-lang.org/issues/11708#change-55580 * Author: Ilya Vorontsov * Status: Open * Priority: Normal * Assignee: Yukihiro Matsumoto ---------------------------------------- It's common to create simple data-object with some constraints. One can either implement custom class or use `Struct`. Struct is generally simpler and helps to avoid some mistakes as non-defined `#hash` and `#eql?`. But at the same time it's more difficult to make validation for `Struct` subclass. ```ruby Point = Struct.new(:x, :y) NonnegativePoint = Struct.new(:x,:y) do def initialize(*args, &block) super raise 'Negative coordinates are not allowed' if x < 0 || y < 0 end end ``` Above written code solves the problem but has one flaw. `Struct.new` creates a subclass of `Struct` and defines some methods as `#x`, `#x=`. And there are no guarantees that `NonnegativePoint#initialize` wasn't redefined too. We can check that `Point.new` without explicitly defined `#initialize` actually hits `Struct#initialize` and `Point#initialize` not defined: ```ruby Point.instance_method(:initialize) # => # NonnegativePoint.instance_method(:initialize) # => # ``` But nothing in `Struct` documentation or test suite states that this behavior can't be changed in newer ruby versions. I propose either to declare in docs and test that initialize method can be safely overriden because `#initialize` is not defined in `Struct` subclasses. In you assume that one day current behavior can change (e.g. for perfomance reasons), then it's reasonable to create an extension point like '#after_initialize' which is called from `Struct`'s subclass `#initialize` method. -- https://bugs.ruby-lang.org/