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

Why does a Ruby toplevel assignment method fail to assign instance variables in the REPL?

Setter 在 Class 内工作;在 REPL 顶级失败

, I was trying to understand why an assignment method was returning an unexpected value, and learned that this is a surprising but documented edge case 中 Ruby。但是,当我试图 调试 问题时,我更深入地了解了兔子洞,运行 发现了一些我无法解释的额外惊喜。

Setter里面一个Class

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

class Setter
  def foo=(bar)
    @foo = Integer(bar).succ
  end
end

然后我从 setter 方法中得到了 return 值的记录奇怪,但实例变量仍然设置正确。例如:

s = Setter.new
s.foo = 1
#=> 1

s.instance_variable_get :@foo
#=> 2

Setter 在 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存储了实例变量,为什么class赋值方法有效而顶层赋值方法失败了?我希望两者的功能相同。

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

在您的 class 示例中,您有一个带有 s.foo = 1 的显式接收器。 Ruby 然后知道你在 s 上调用 foo= setter。 assignment methods documentation 表示:

When using method assignment you must always have a receiver. If you do not have a receiver, Ruby assumes you are assigning to a local variable[.]