为什么Ruby toplevel赋值方法无法在REPL中分配实例变量?

时间:2016-11-04 17:42:51

标签: ruby setter instance-variables read-eval-print-loop toplevel

Setter在一个班级内工作;在REPL顶级

中失败

related question中,我试图理解为什么赋值方法返回了一个意外的值,并且知道这是Ruby中的surprising but documented edge case。然而,当我试图调试这个问题时,我进一步深入兔子洞并遇到了一些我无法解释的额外惊喜。

Setter Inside Class

当我在类中有一个setter方法时,例如:

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)中,实例变量实际上从未被设置,即使我的理解是实例变量应该存储在顶层“主”对象中:

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
#=> []

然而,toplevel对象 存储实例变量!例如:

@bar = 1 + 1; @bar
#=> 2

instance_variable_get :@bar
#=> 2

问题,重述

鉴于REPL存储实例变量,为什么类赋值方法在顶层赋值方法失败时有效?我希望两者的功能相同。

1 个答案:

答案 0 :(得分:1)

如果你没有明确地写出接收器,那么Ruby的赋值运算符=将创建一个局部变量。在你的情况下:

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的显式接收器。然后Ruby知道你在foo=上调用s setter。 assignment methods documentation说:

  

使用方法分配时,您必须始终拥有接收器。如果您没有接收器,Ruby假定您正在分配一个局部变量[。]