Issue #11923 has been updated by Ilya Vorontsov.

Assignee changed from Marc-Andre Lafortune to ruby-core

Sorry. Don't how to choose appropriate assignee.

----------------------------------------
Feature #11923: Put Struct accessors into separate module to allow redefining them in Struct.new's block
https://bugs.ruby-lang.org/issues/11923#change-55843

* Author: Ilya Vorontsov
* Status: Open
* Priority: Normal
* Assignee: ruby-core
----------------------------------------
`Struct.new(*args, &block)` creates a subclass of `Struct`. Some methods like accessors are defined right in newly created class. Others like `#initialize`, `#values` and so on are declared in a superclass, i.e. in `Struct`.
The fact that `#initialize` is defined in a superclass allows redefining constructor as was proposed in [#11708](https://bugs.ruby-lang.org/issues/11708). But it is not that simple to redefine accessors (and documentation gives no clue which methods are to be redefined and which aren't):

```ruby
Point=Struct.new(:x,:y) do
  def x=(val)
    puts "set x = #{val}"
    super
  end
end

pt = Point.new(1,2)
pt.x = 3 # => NoMethodError: super: no superclass method `x=' for #<struct Point x=1, y=2>
```

As accessors are defined just inside a class `Point`, we can't use `super`. To correctly implement desired logging behavior of `#x=` one need to create a module with `#x=` method and to prepend it in front of class.

I propose instead one of two solutions which overcome this issue:
1. (worse) `Struct.new` when takes a block should not evaluate it in a context of created class. Instead it should create an anonymous module, evaluate block in its context (so that module gets all defined methods) and prepend that module in front of created class. But this possibly can conflict with reopening a class.
2. (better) `Struct.new` should create an anonymous module, define struct accessors in it and then include a module into created class. Then block should be evaluated in context of the class as it's done now. So `super` invocation in `#x=` definition from a block supplied to `Struct.new` will hit method in included module instead of `NoMethodError`.

There is one possible drawbacks of implementing this (I could overlook some other problems): cutting perfomance due to additional module in ancestors chain.



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>