Почему метод назначения верхнего уровня Ruby не может назначить переменные экземпляра в REPL?
Сеттер работает внутри класса; Сбои в REPL Top-Level
В связанном вопросе я пытался понять, почему метод присваивания возвращал неожиданное значение, и узнал, что это удивительный, но задокументированный крайний случай в Ruby. Однако, когда я пытался отладить проблему, я пошел дальше по кроличьей норе и столкнулся с некоторыми дополнительными сюрпризами, которые я не могу объяснить.
Сеттер внутри класса
Когда у меня есть метод установки внутри класса, такой как:
class Setter
def foo=(bar)
@foo = Integer(bar).succ
end
end
затем я получаю задокументированную странность с возвращаемыми значениями из метода setter, но переменная экземпляра по-прежнему установлена правильно. Например:
s = Setter.new
s.foo = 1
#=> 1
s.instance_variable_get :@foo
#=> 2
Сеттер в объекте верхнего уровня REPL
Однако в REPL (например, Pry или IRB) переменная экземпляра фактически никогда не устанавливается, хотя я понимаю, что переменные экземпляра должны храниться в верхнем уровне "main" объекта:
self.name
#=> NoMethodError: undefined method `name' for main:Object
# This is expected to set the @foo instance variable for main.
def foo= int
@foo = int
end
foo = 1
@foo
#=> nil
instance_variable_get :@foo
#=> nil
TOPLEVEL_BINDING.eval('self').instance_variables
#=> []
И все же объект верхнего уровня хранит переменные экземпляра! Например:
@bar = 1 + 1; @bar
#=> 2
instance_variable_get :@bar
#=> 2
Вопрос, пересчитанный
Учитывая, что REPL хранит переменные экземпляра, почему метод присваивания класса работает, когда метод присвоения верхнего уровня не выполняется? Я ожидаю, что оба будут функционировать одинаково.
1 ответ
Оператор присваивания Руби =
создаст локальную переменную, если вы явно не выписываете получателя. В твоем случае:
foo = 1
создает локальную переменную foo, а не вызывает метод foo=
, Вам придется использовать
self.foo = 1
На самом деле вызвать метод, который вы определили выше. Теперь это установит @foo:
def foo= i # define foo= on self
@foo = i
end
#=> :foo=
foo = 3
#=> 3
@foo
#=> nil
foo # here's the new local variable
#=> 3
instance_variables
#=> [:@prompt]
instance_variable_get :@foo
#=> nil
self.foo = 4 # now calling the foo= method
#=> 4
foo # local foo is still 3
#=> 3
@foo # now the ivar is set
#=> 4
В вашем примере класса у вас есть явный получатель с s.foo = 1
, Руби знает, что ты звонишь foo=
установить на s
, Документация по методам назначения гласит:
При использовании метода назначения у вас всегда должен быть получатель. Если у вас нет получателя, Ruby предполагает, что вы присваиваете локальную переменную [.]