Почему "instance.send(:initialize, *args, **kwargs, &block)" терпит неудачу только изнутри Class#new?

Я застрял на этом довольно давно. Взгляните на это:

class SuperClass
  def self.new(*args, **kwargs, &block)
    i = allocate()

    # Extra instance setup code here

    i.send(:initialize, *args, **kwargs, &block)
    return i
  end
end

class Test < SuperClass
  def initialize
    puts "No args here"
  end
end

Класс SuperClass в основном "переопределение" по умолчанию new метод, так что некоторая дополнительная инициализация может произойти до initialize,

Теперь следующее работает просто отлично:

t = Test.allocate
t.send(:initialize, *[], **{}, &nil)

Однако это не так:

t = Test.new
 ArgumentError: неверное количество аргументов (1 для 0)
from (pry):7: в `initialize' 

Это терпит неудачу на этой линии в SuperClass:

i.send(:initialize, *args, **kwargs, &block)

Но, по-видимому, это только сбой, если вызывается new метод. Я подтвердил, что args == [], kwargs == {} а также block == nil,

Кто-нибудь может объяснить это?


Рубиновая версия:

 ruby 2.2.3p173 (2015-08-18, редакция 51636) [x86_64-linux] 

Пожалуйста, воздержитесь от предложения, чтобы я не перегружал Class.new , Я знаю, что могу использовать Class.inherited а также Class.append за тот же результат. Этот вопрос только о том, почему звонок initialize выходит из строя.

2 ответа

Давайте рассмотрим более простой пример, особенно потому, что проблема не столь специфична, как вопрос, и его название заставляет его выглядеть, но убедитесь сами.

def m   # takes no arguments
end
m(**{}) # no argument is passed
h = {}
m(**h)  # an argument is passed => ArgumentError is raised

Это несоответствие было введено в 2.2.1 коммитом, предназначенным для исправления ошибки сегментации, включающей **{} ( Ошибка № 10719). Совершать особые случаи **{} не передавать аргумент. Другие способы, такие как **Hash.new а также h={};**h все еще передать пустой хэш в качестве аргумента.

Предыдущие версии последовательно поднимают ArgumentError ( демо). Я могу ошибаться, но я верю, что это предполагаемое поведение. Однако это может или не может быть тем, кто на самом деле хочет. Так что если вы думаете, что двойное разделение пустого хэша не должно передавать аргумент (например, **{} на данный момент) и, следовательно, работают подобно разбрызгиванию пустого массива, об этом есть открытая проблема ( ошибка №10856). Также упоминается это относительно новое несоответствие.

Просто *args будет захватывать все аргументы, включая ключевые аргументы, в случае, если вам не нужно ссылаться kwargs отдельно в new метод:

class SuperClass
  def self.new(*args, &block)
    i = allocate

    # Extra instance setup code here

    i.send(:initialize, *args, &block)
    i
  end
end
Другие вопросы по тегам