Ruby的双冒号`::`是什么?


426

这是什么双冒号::?例如Foo::Bar

我找到一个定义

::是一元运算符,其允许:一个类或模块中定义的,常数,实例方法和类方法从类或模块以外的任何地方进行访问。

如果仅使用::公开任何内容,作用域(私有,受保护)有什么好处?


175
为了将来的Google员工的利益,如果您要搜索符号,请尝试symbolhound.com
Andrew Grimm



6
祝福你,@ AndrewGrimm。这是我本周见过的最好的东西。
abeger

Answers:


381

::基本上是名称空间解析运算符。它允许您访问模块中的项目或类中的类级别的项目。例如,假设您有以下设置:

module SomeModule
    module InnerModule
        class MyClass
            CONSTANT = 4
        end
    end
end

您可以CONSTANT从模块外部访问SomeModule::InnerModule::MyClass::CONSTANT

它不会影响在类上定义的实例方法,因为您可以使用不同的语法(点.)来访问它们。

相关说明:如果要返回顶级命名空间,请执行以下操作::: SomeModule – Benjamin Oakes


5
例如,在C#中,是。另一方面,C ++(和Ruby)::用于名称空间解析,例如std::cout << "Hello World!";
Jerry Fernholz

142
相关说明:如果要返回顶级名称空间,请执行以下操作: ::SomeModule
Benjamin Oakes 2010年

5
@Benjamin隐含前导冒号,除非我碰巧在另一个模块中有SomeModule并且我想获取顶级模块,对吗?
乔·利斯

7
@乔是的。如果要确保引用顶级命名空间中的常量或在另一个模块中具有相同名称的常量(例如:: SomeOtherModule :: ClassMethods),这可能会有所帮助。
本杰明·奥克斯

2
这非常类似于C ++的范围操作数
lkahtz 2012年

111

这个简单的例子说明了这一点:

MR_COUNT = 0        # constant defined on main Object class
module Foo
  MR_COUNT = 0
  ::MR_COUNT = 1    # set global count to 1
  MR_COUNT = 2      # set local count to 2
end

puts MR_COUNT       # this is the global constant: 1
puts Foo::MR_COUNT  # this is the local constant: 2

取自http://www.tutorialspoint.com/ruby/ruby_operators.htm


这是导致警告的原因。有没有办法逃避警告?
NullVoxPopuli

3
@NullVoxPopuli通常,修改常量是一件很糟糕的事情,但是,例如,如果您想修改写得不好的gem中的常量并且不想派生,可以通过在定义模块的模块上使用.send(:remove_const)来完成。它,然后重新定义常数。
BookOfGreg 2014年

71

::使您可以访问在另一个类或模块内定义的常量,模块或类。它用于提供名称空间,以使方法和类名不会与不同作者的其他类冲突。

当您ActiveRecord::Base在Rails中看到它时,意味着Rails具有类似

module ActiveRecord
  class Base
  end
end

Base在模块内部调用的类ActiveRecord,然后将该类引用为ActiveRecord::Base(您可以在activerecord-nnn / lib / active_record / base.rb的Rails源代码中找到该类)

::的常见用法是访问模块中定义的常量,例如

module Math
  PI = 3.141 # ...
end

puts Math::PI

::运营商不允许你的方法绕过可视性标记为私有或保护。


7
因此,如果具有class MyClass < ActiveRecord::Base,是否意味着MyClass仅继承类的方法,而不继承ActiveRecord模块中的任何方法?
查理·帕克

2
为什么要为此名称空间解析使用特殊的双冒号而不是使用“。”。还为此吗?即使我们使用“。”,上下文和大写字母也可以防止含义的混淆,不是吗?
乔纳

3
@Jonah在某些情况下可能会模棱两可。例如,考虑class Foo; Baz = 42; def self.Baz; "Baz method!"; end; end(完全有效)Foo::Baz # => 42Foo.Baz # => "Baz method!"。注意Foo::Baz()(带括号)也会调用该方法。
mikej 2015年

3
因此,用例解决了具有类常量和具有完全相同名称的类方法的能力吗?似乎不太赞成该功能。就我个人而言,我宁愿失去这种能力(无论如何似乎还是麻烦),失去双冒号并使用“”。也可以用于命名空间...。也许还有其他用例可以解决?
乔纳2015年

26

如果仅使用::公开任何内容,范围(私有,受保护)有什么用?

在Ruby中,所有内容都可以公开,并且可以在其他任何地方进行修改。

如果您担心类可以从“类定义”之外进行更改的事实,那么Ruby可能不适合您。

另一方面,如果您对Java的类被锁定感到沮丧,那么Ruby可能就是您想要的。


1
我听说有些红宝石学家说实例变量没有公开,甚至attr_accessor只是使方法修改了变量。(然后再来instance_eval
安德鲁·格林

4
是的,有instance_eval。但是还有instance_variable_getinstance_variable_set。Ruby对于约束来说太动态了。
yfeldblum

12

除了以前的答案外,它是::用于访问实例方法的有效Ruby 。以下所有条件均有效:

MyClass::new::instance_method
MyClass::new.instance_method
MyClass.new::instance_method
MyClass.new.instance_method

根据最佳实践,我认为仅建议使用最后一个。


11

不,它不是访问所有方法,而是一个“分辨率”运算符,也就是说,您可以使用它来解析恒定/静态符号的范围(可以说的位置)。

例如,在您的第一行中,Rails使用它在ActiveRecord.Module中找到基类,在第二行中,它用于查找Routes类的类方法(静态),等等。

它不用于公开任何内容,而是用于在示波器范围内“定位”东西。

http://en.wikipedia.org/wiki/Scope_resolution_operator


“(静态)”是指“(绘制)”吗?!?
Meltemi 2010年

8

出乎意料的是,这里的所有10个答案都说相同的话。'::'是名称空间解析运算符,是的。但是当涉及到常量查找算法时,您必须意识到名称空间解析运算符。正如Matz在他的《 Ruby编程语言》一书中所描述的那样,持续查找有多个步骤。首先,它在引用该常量的词法范围内搜索常量。如果未在词法范围内找到该常量,则将搜索继承层次结构。由于采用了这种恒定查找算法,因此我们在下面得到了预期的结果:

module A
  module B
      PI = 3.14
      module C
        class E
          PI = 3.15
        end
        class F < E
          def get_pi
            puts PI
          end
        end
      end
  end
end
f = A::B::C::F.new
f.get_pi
> 3.14

尽管F继承自E,但是B模块在F的词法范围内。因此,F实例将引用模块B中定义的常量PI。现在,如果模块B未定义PI,则F实例将引用PI超类E中定义的常数。

但是,如果我们使用'::'而不是嵌套模块呢?我们会得到相同的结果吗?没有!

通过在定义嵌套模块时使用名称空间解析运算符,嵌套模块和类不再位于其外部模块的词法范围内。如下所示,A :: B中定义的PI不在A :: B :: C :: D的词法范围内,因此,当尝试在get_pi实例方法中引用PI时,我们得到未初始化的常量:

module A
end

module A::B
  PI = 3.14
end

module A::B::C
  class D
    def get_pi
      puts PI
    end
  end
end
d = A::B::C::D.new
d.get_pi
NameError: uninitialized constant A::B::C::D::PI
Did you mean?  A::B::PI

4

这是关于防止定义与项目中链接的其他代码冲突的全部内容。这意味着您可以将事情分开。

例如,您的代码中可以有一个称为“运行”的方法,您仍然可以调用该方法,而不必调用已链接到其他库中的“运行”方法。


3
module Amimal
      module Herbivorous
            EATER="plants" 
      end
end

Amimal::Herbivorous::EATER => "plants"

::用于创建范围。为了从2个模块访问Constant EATER,我们需要调整模块的范围以达到常量


3

Ruby on Rails ::用于名称空间解析。

class User < ActiveRecord::Base

  VIDEOS_COUNT = 10
  Languages = { "English" => "en", "Spanish" => "es", "Mandarin Chinese" => "cn"}

end

要使用它:

User::VIDEOS_COUNT
User::Languages
User::Languages.values_at("Spanish") => "en"

另外,其他用法是:使用嵌套路由时

OmniauthCallbacksController 在用户下定义。

并路由为:

devise_for :users, controllers: {omniauth_callbacks: "users/omniauth_callbacks"}


class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

end
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.