Що таке двокрапка двокрапки `::` Рубі?


427

Що це за двокрапка ::? Напр Foo::Bar.

Я знайшов визначення :

Це ::одинарний оператор, який дозволяє отримувати доступ до констант, методів екземплярів та методів класу, визначених у класі чи модулі, з будь-якого місця за межами класу чи модуля.

Яка користь - це сфера (приватна, захищена), якщо ви можете просто використати, ::щоб викрити щось?


175
На користь майбутніх гуглерів, якщо ви намагаєтеся шукати символ, спробуйте symbolhound.com
Ендрю Грімм

1


6
Благослови, @AndrewGrimm. Це найкраще, що я бачив цього тижня.
abeger

Відповіді:


381

::в основному є оператором роздільної здатності простору імен. Це дозволяє отримувати доступ до елементів у модулях або до елементів класу на класах. Наприклад, скажіть, що у вас була така настройка:

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

Ви можете отримати доступ CONSTANTіз зовнішнього модуля як SomeModule::InnerModule::MyClass::CONSTANT.

Це не впливає на методи екземпляра, визначені в класі, оскільки ви отримуєте доступ до тих, хто має інший синтаксис (крапка .).

Відповідна примітка: Якщо ви хочете повернутися до простору імен вищого рівня, зробіть це: :: SomeModule - Бенджамін Оукс


5
Наприклад, у C # так. З іншого боку, C ++ (і Ruby) використовують ::для вирішення простору імен, таких якstd::cout << "Hello World!";
Джеррі Фернхольз

142
Відповідна примітка: Якщо ви хочете повернутися до простору імен вищого рівня, зробіть це: ::SomeModule
Бенджамін Оукс

5
@Benjamin Провідні колони маються на увазі, якщо я не маю SomeModule в іншому модулі, і я хочу отримати замість нього верхній рівень, правильно?
Джо Лісс

7
@Jo Так. Це може бути корисно, якщо ви хочете переконатися, що ви посилаєтесь на константу на просторі імен верхнього рівня або константу з тим самим іменем в іншому модулі (наприклад: SomeOtherModule :: ClassMethods).
Бенджамін Оукс

2
Це дуже схоже на область операндів C ++
lkahtz

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 Як правило, зміна констант - це дуже погана річ, але якщо ви, наприклад, хочете змінити константу в погано написаному самоцвіті, а не хочете розщедрити його, це можна зробити, використовуючи .send (: remove_const) для модуля, який визначає. це, потім перевизначаючи константу.
BookOfGreg

71

::Дозволяє отримати доступ до константи, модуля або класу, визначених всередині іншого класу або модуля. Він використовується для надання просторів імен, щоб імена методів і класів не суперечили іншим класам різних авторів.

Коли ви бачите ActiveRecord::Baseв Rails, це означає, що Rails має щось подібне

module ActiveRecord
  class Base
  end
end

тобто клас, який називається Baseвсередині модуля, на ActiveRecordякий потім посилається як ActiveRecord::Base(ви можете знайти це у джерелі Rails у activerecord-nnn / lib / active_record / base.rb)

Поширене використання :: - це доступ до констант, визначених у модулях, наприклад

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 # => 42та Foo.Baz # => "Baz method!". Зауважте, що Foo::Baz()(з дужками) також буде викликати метод.
mikej

3
Отже, випадок використання вирішує це можливість мати константу класу та метод класу, які мають точно таку ж назву? Це не здається сильним аргументом на користь функції. Особисто я набагато скоріше втрачаю цю здатність (все одно здається проблемою), втрачаю подвійну двокрапку та використовую "". для простору імен теж .... Можливо, є додаткові випадки використання, які він вирішує?
Йона

26

Яке благо має сфера (приватна, захищена), якщо ви можете просто використовувати ::, щоб викрити щось?

У Рубі все виставлено, і все можна змінити з будь-якого місця.

Якщо ви переживаєте з приводу того, що класи можна змінити за межами "визначення класу", то, напевно, Рубі не для вас.

З іншого боку, якщо ви засмучені тим, що класи Java закриті, то Ruby - це, напевно, те, що ви шукаєте.


1
Я чув, як деякі рубіністи говорять про те, що змінні екземплярів не піддаються впливу, що навіть attr_accessorпросто робить методи, що змінюють змінну. (Потім знову є instance_eval)
Ендрю Грімм

4
Правильно, є instance_eval. Але є також instance_variable_getі instance_variable_set. Рубі просто занадто динамічний для обмежень.
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


під "(статичний)" ти маєш на увазі "(малювати)"?!?
Мелтемi

8

Дивно, але всі 10 відповідей тут говорять те саме. '::' - це оператор роздільної здатності простору імен, і так, це правда. Але є одна готча, яку ви повинні усвідомити щодо оператора роздільної здатності простору імен, якщо мова йде про алгоритм постійного пошуку . Як окреслює Мац у своїй книзі "Мова програмування Рубі", постійний пошук має кілька кроків. По-перше, він шукає константу в лексичній області, де посилається на постійну. Якщо вона не знаходить константу в межах лексичної сфери, то вона здійснює пошук ієрархії спадкування . Через цей алгоритм постійного пошуку нижче ми отримуємо очікувані результати:

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 екземпляри будуть посилатися на постійну PI, визначену в модулі B. Тепер, якщо модуль B не визначив PI, то F екземпляри будуть посилатися на PI константа, визначена в суперкласі E.

Але що робити, якщо ми використовували "::", а не гніздові модулі? Чи отримали б ми такий самий результат? Ні!

Використовуючи оператор роздільної здатності простору імен при визначенні вкладених модулів, вкладені модулі та класи вже не входять до лексичної сфери їх зовнішніх модулів. Як ви бачите нижче, PI, визначений у A :: B, не перебуває в лексичному просторі A :: B :: C :: D, і, таким чином, ми отримуємо неініціалізовану константу при спробі звернутися до PI у методі екземпляра get_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

Вся справа в тому, щоб запобігти зіткненню визначень з іншим кодом, пов'язаним з вашим проектом. Це означає, що ви можете тримати речі окремо.

Наприклад, у вашому коді може бути один метод, званий "run", і ви все одно зможете викликати свій метод, а не метод "run", визначений в іншій бібліотеці, до якої ви пов’язані.


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

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

:: використовується для створення області. Для того, щоб отримати доступ до постійного EATER з двох модулів, нам потрібно охопити модулі, щоб досягти постійної


3

Рубін на рейках використовує ::для вирішення простору імен.

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.