Project

General

Profile

Actions

Bug #19362

open

#dup on Proc doesn't call initialize_dup

Added by zverok (Victor Shepelev) 5 months ago. Updated 3 months ago.

Status:
Open
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:111953]

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) 5 months ago

While String.allocate and Array.allocate are defined, Proc.allocate is not.
What do you expect as self of Proc#initialize_dup?

Actions #2

Updated by nobu (Nobuyoshi Nakada) 5 months ago

  • Status changed from Open to Feedback

Updated by zverok (Victor Shepelev) 5 months 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) 5 months ago

  • Status changed from Feedback to Open

Updated by zverok (Victor Shepelev) 4 months 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) 4 months ago

I'm not sure if this is a bug or intentional.
Please add this to [Misc #19429].

Updated by matz (Yukihiro Matsumoto) 3 months 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.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like1Like0