在 Ruby 中,大多数未初始化甚至不存在的变量的计算结果为nil
. 这适用于局部变量、实例变量和全局变量:
defined? foo #=> nil
local_variables #=> []
if false
foo = 42
end
defined? foo #=> 'local-variable'
local_variables #=> [:foo]
foo #=> nil
foo.nil? #=> true
defined? @bar #=> nil
instance_variables #=> []
@bar #=> nil
@bar.nil? #=> true
# warning: instance variable @bar not initialized
defined? $baz #=> nil
$baz #=> nil
# warning: global variable `$baz' not initialized
$baz.nil? #=> true
# warning: global variable `$baz' not initialized
但是,对于类层次结构变量和常量,情况并非如此:
defined? @@wah #=> nil
@@wah
# NameError: uninitialized class variable @@wah in Object
defined? QUUX #=> nil
QUUX
# NameError: uninitialized constant Object::QUUX
这是一条红鲱鱼:
defined? fnord #=> nil
local_variables #=> []
fnord
# NameError: undefined local variable or method `fnord' for main:Object
在这里出现错误的原因不是未初始化的局部变量不计算为nil
,而是fnord
模棱两可:它可能是发送到默认接收器的无参数消息(即等效于self.fnord()
)或访问局部变量fnord
。
为了消除歧义,您需要添加一个接收者或一个参数列表(即使是空的)来告诉 Ruby 这是一个消息发送:
self.fnord
# NoMethodError: undefined method `fnord' for main:Object
fnord()
# NoMethodError: undefined method `fnord' for main:Object
或者确保解析器(而不是评估器)在使用之前解析(不执行)赋值,告诉 Ruby 它是一个局部变量:
if false
fnord = 42
end
fnord #=> nil
为什么实例变量的处理方式与局部变量和类变量不同?
其实不是。它被视为与局部变量相同。类层次结构变量的行为不同,局部变量、实例变量和全局变量的行为都相同。
还有其他原因……类变量也不能像那样表现吗?
我不知道。对于实例变量,它非常方便,因为与 Java 不同,例如,实例变量在类定义中声明,因此类的每个实例始终存在,在 Ruby 中,实例变量不在任何地方声明。一旦被分配,它们就会神奇地出现。由于不一定保证实例变量存在,因此编写使用实例变量的方法如果抛出异常会很痛苦。
为什么类层次结构变量不同,我不知道。也许是因为无论如何都没有人使用它们,或者因为它们通常倾向于在类主体中初始化,并且在未初始化时根本无法访问。