Feature #19362
closed#dup on Proc doesn't call initialize_dup
Description
In #17545, #dup had changed to create an instance of the subclass.
It, though, doesn't invoke initialize_dup of the subclass, unlike other standard classes.
class MyAry < Array
def initialize_dup(...)
p(self.class, ...)
super
end
end
class MyString < String
def initialize_dup(...)
p(self.class, ...)
super
end
end
class MyProc < Proc
def initialize_dup(...)
p(self.class, ...)
super
end
end
MyString.new('test').dup # prints MyString, "test"
MyAry.new(['test']).dup # prints MyAry, ["test"]
MyProc.new { 'test' }.dup # doesn't print anything
This makes the change in #17545 useless: while inheriting from core classes is indeed marginal, one of author's intention might be carrying additional information with the Proc instance, and bypassing #initialize_dup makes it impossible to maintain this information.
It seems that actually #initialize_dup is also invoked on the core classes themselves, but ignored on Proc.
class Array
def initialize_dup(...)
p(self.class, ...)
super
end
end
class String
def initialize_dup(...)
p(self.class, ...)
super
end
end
class Proc
def initialize_dup(...)
p(self.class, ...)
super
end
end
'test'.dup # prints String, "test"
['test'].dup # prints Array, ["test"]
Proc.new { 'test' }.dup # doesn't print anything
Which is an even more marginal problem but still an inconsistency.
Updated by nobu (Nobuyoshi Nakada) almost 3 years ago
While String.allocate and Array.allocate are defined, Proc.allocate is not.
What do you expect as self of Proc#initialize_dup?
Updated by nobu (Nobuyoshi Nakada) almost 3 years ago
- Status changed from Open to Feedback
Updated by zverok (Victor Shepelev) almost 3 years ago
What do you expect as self of
Proc#initialize_dup?
@nobu (Nobuyoshi Nakada) "the current proc object", like in any initialize[...]. I am not sure I understand why is it impossible. From layman's point of view, those two should work the same way:
class TaggedString < String
attr_reader :tag
def initialize(val, tag:)
super(val)
@tag = tag
end
def initialize_dup(other)
super
@tag = other.tag
end
end
class TaggedProc < Proc
attr_reader :tag
def initialize(tag, &block)
super(&block)
@tag = tag
end
def initialize_dup(other)
super
@tag = other.tag
end
end
s = TaggedString.new('test', tag: 'foo')
p s.tag #=> 'foo'
p s.dup.tag #=> 'foo'
t = TaggedProc.new('test') { }
p t.tag #=> 'test'
p t.dup.tag #=> expected 'test', got nil
Otherwise, I am not sure what's the semantics of Proc#dup and how it is supposed to be used.
And if it isn't supposed to, it might make sense to prohibit its usage or at least document the quirks?..
Updated by nobu (Nobuyoshi Nakada) almost 3 years ago
- Status changed from Feedback to Open
Updated by zverok (Victor Shepelev) over 2 years ago
@nobu (Nobuyoshi Nakada) Thanks! Will this PR be merged? (I secretly hoped to have it in 3.2.1 :)))
Updated by nobu (Nobuyoshi Nakada) over 2 years ago
I'm not sure if this is a bug or intentional.
Please add this to [Misc #19429].
Updated by matz (Yukihiro Matsumoto) over 2 years ago
Accepted, mostly for the sake of consistency. As a general suggestion, making subclasses from built-in classes is not a good idea in Ruby, though.
Matz.
Updated by nobu (Nobuyoshi Nakada) almost 2 years ago
I've forgot this, but isn't this a feature require but not a bug?
Updated by nobu (Nobuyoshi Nakada) almost 2 years ago
- Tracker changed from Bug to Feature
- Backport deleted (
2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN)
Updated by nobu (Nobuyoshi Nakada) almost 2 years ago
- Status changed from Open to Closed
Applied in changeset git|1507118f0b70fc8002b4b0f186b464c64965cd1e.
[Feature #19362] Call #initialize_dup hook at Proc#dup