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 2 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 2 years ago
- Status changed from Open to Feedback
Updated by zverok (Victor Shepelev) almost 2 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 2 years ago
- Status changed from Feedback to Open
Updated by zverok (Victor Shepelev) over 1 year 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 1 year ago
I'm not sure if this is a bug or intentional.
Please add this to [Misc #19429].
Updated by matz (Yukihiro Matsumoto) over 1 year 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) about 1 year ago
I've forgot this, but isn't this a feature require but not a bug?
Updated by nobu (Nobuyoshi Nakada) about 1 year 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) about 1 year ago
- Status changed from Open to Closed
Applied in changeset git|1507118f0b70fc8002b4b0f186b464c64965cd1e.
[Feature #19362] Call #initialize_dup
hook at Proc#dup