Ruby 常量查找 情景1:

Ruby 常量查找 情景1:,ruby,Ruby,在下面的示例中,放置Post::User.foo行打印foo。换句话说,Post::User返回一个全局User常量 class User def self.foo "foo" end end class Post puts Post::User.foo end # => warning: toplevel constant User referenced by Post::User # => foo 情景2: 第二个示例引发错误,因为找不到常量 module

在下面的示例中,
放置Post::User.foo
行打印
foo
。换句话说,
Post::User
返回一个全局
User
常量

class User
  def self.foo
    "foo"
  end
end

class Post
  puts Post::User.foo
end
# => warning: toplevel constant User referenced by Post::User
# => foo
情景2: 第二个示例引发错误,因为找不到常量

module User
  def self.foo
    "foo"
  end
end

module Post
  puts Post::User.foo
end
# => uninitialized constant Post::User (NameError)
场景2的结果更直观。为什么会在场景1中找到常数?如果在场景1中返回了
User
常量,为什么在场景2中没有发生这种情况?在场景#2中,
Post
是一个模块,因此在这种情况下,应该在
对象中搜索常量。祖先
,它也应该返回
用户
常量,但这不会发生

**ruby中的
class
关键字实际上是一个方法名,它接受常量并为其定义一个类

在场景1中,当调用
put Post::User.foo
时。Ruby查看是否定义了类
Post::User
(Ruby将其作为常量进行搜索,因为它就是这样)。一旦找到它,就会调用foo方法

但是在场景2中,您已经在模块内部定义了它,因为调用了
put Post::User.foo
,并且不存在
Post::User
这样的类。搜索失败,您会收到明显的错误消息


您可以参考链接中的“类名是常量”部分了解更多详细信息。

我认为问题在于类和模块()的继承级别不同。事实上,类对象是从模块对象继承的。被仰视的祖先

module A; end
p A.ancestors   #=> [A]

class B; end
p B.ancestors   #=> [B, Object, Kernel, BasicObject]
这意味着如果查找算法步进
模块A
,则无法步出

因此,在
模块Post
的情况下,
Post::User
的查找算法如下

  • 查找常量
    Post
    (查找
    模块Post
  • 模块Post
    中查找常量
    用户
    (未找到,请查找
    Post
    的祖先)
  • 死胡同错误
如果是
类帖子

  • 查找常量
    Post
    (查找
    类Post
  • 类帖子
    中查找常量
    用户
    (未找到,请查找
    帖子的祖先
  • 查找常量
    用户
    (查找
    类用户
    ,带有警告)
这就是为什么可以在一个名称空间级别中链接类,ruby仍然可以找到它们。如果你尝试

class User
  def self.foo
    "foo"
  end
end

class A1; end

class A2; end

class Foo
  p Foo::A1::A2::User.foo
end
#... many of warnings
#> "foo"
它仍然很好。 及

因为查找算法步进了模块,被困住了

TL;DR

class User
  def self.foo
    "foo"
  end
end

class Post1
  Post1.tap{|s| p s.ancestors}::User.foo #=> [Post1, Object, Kernel, BasicObject]
end
# ok

module Post2
  Post2.tap{|s| p s.ancestors}::User.foo #=> [Post2]
end
# error
已更新

在这种情况下,调用Post::User.foo的位置起不了多大作用。它也可能在类/模块之外,其行为将是相同的

module ModuleA; end
class ClassB;  end
class ClassC;  end

class E;  ModuleA::ClassC; end   # error
module F; ClassB::ClassC;  end   # ok

ClassB::ClassC    # ok
ClassB::ModuleA   # ok
ModuleA::ClassB   # error
正如你所指出的

当然,模块或类的祖先不同,但在 模块,则会在Object.Founders中执行额外的常量查找

它只在“初始”查找时发生。这意味着如果

module Post; Post::User.foo; end
const
Post
将首先被查找(在这里我可能会出错)
Post.concenters
,因为没有
Post::Post
,所以继续在
Object.concenters
中查找(然后它将使用我在顶部描述的算法)

总结一下,您称之为常量的上下文仅对第一次(最左边)常量查找起作用。然后只考虑剩下的对象

A::B::C
A        # consider context
 ::B     # consider only A
    ::C  # consider only B

这种行为是由模块内部而不是类中的查找引起的。(事实上,您正在查找模块内的模块和类内的类是不相关的)

如果您查看C代码,这两个函数都调用了exclude=true、recurse=true、visibility=true

对于A::M,它将查找:

tmp = A
tmp::M  # (doesn't exist)
tmp = tmp.super # (tmp = Object) 
tmp::M  # (exists, but warns).
tmp = B
tmp::M # (doesn't exist)
tmp = tmp.super # (modules have no super so lookup stops here)
在B::M的情况下,它将查找:

tmp = A
tmp::M  # (doesn't exist)
tmp = tmp.super # (tmp = Object) 
tmp::M  # (exists, but warns).
tmp = B
tmp::M # (doesn't exist)
tmp = tmp.super # (modules have no super so lookup stops here)

由于exclude为true,因此跳过了模块跳转到(
tmp=Object
)的常见边缘情况。这意味着
B::M
的行为与
模块B不同;MReby中的一个不一致性,即:

< P>首先,请考虑在类<代码>对象< /代码>中定义所有<强> >顶级< /强>常量,因为Ruby是一种面向对象语言,不存在不属于某类的变量或常量:

class A; end
module B; end

A == Object::A # => true 
B == Object::B # => true
其次,类
对象
默认为任何类的祖先,但不是模块的祖先:

class A; puts ancestors; end # => [A, Object, Kernel, BasicObject]
module B; puts ancestors; end # => []
同时,
模块中都没有
对象
。嵌套

class A; puts Module.nesting; end # => [A]
module B; puts Module.nesting; end # => [B]
然后,的第7.9章说Ruby在
模块.nesting中搜索任何常量,然后在
祖先中搜索

因此,在您的示例中,它为类
Post
查找常量
User
,因为Ruby在类
对象中定义了顶级常量
User
(如
对象::User
)。而
对象
Post
祖先

Object::User == Post::User # => true
但是在
祖先
模块中没有
对象
。常量
Post
在类
Object
中定义为
Object::Post
,但它不是从
Object
派生的,因为
模块Post
不是对象。因此,它不会通过它的
祖先
解析模块中的常量
用户

同时,如果您将
User
保留为
模块
,并将
Post
转换为一个类,则此功能将起作用:

module User
  def self.foo
    "foo"
  end
end

class Post
  puts Post::User.foo
end

# => foo

这是因为
类Post
可以解析其超类
对象中的任何常量,并且所有顶级常量都是在
对象中定义的

我怀疑这与模块
Post的情况有关。祖先
不包括
对象
这一事实有关