Was ist Rubys Doppelpunkt `::`?

426

Was ist das für ein Doppelpunkt ::? ZB Foo::Bar.

Ich habe eine Definition gefunden :

Das ::ist ein unärer Operator, der ermöglicht: Konstanten, beispielsweise Verfahren und Methoden der Klasse in einer Klasse oder das Modul definiert ist , von jedem beliebigen Ort außerhalb der Klasse oder des Moduls zugegriffen werden kann.

Was nützt der Umfang (privat, geschützt), wenn Sie nur ::etwas aussetzen können?

Meltemi
quelle
175
Wenn Sie versuchen, nach einem Symbol zu suchen, versuchen Sie es für zukünftige Googler mit symbolhound.com
Andrew Grimm
6
Segne dich, @AndrewGrimm. Das ist das Beste, was ich diese Woche gesehen habe.
Abeger

Antworten:

381

::ist im Grunde ein Namespace-Auflösungsoperator. Sie können auf Elemente in Modulen oder auf Elemente auf Klassenebene in Klassen zugreifen. Angenommen, Sie hatten dieses Setup:

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

Sie können CONSTANTvon außerhalb des Moduls als zugreifen SomeModule::InnerModule::MyClass::CONSTANT.

Es wirkt sich nicht auf Instanzmethoden aus, die für eine Klasse definiert sind, da Sie auf solche mit einer anderen Syntax (dem Punkt .) zugreifen .

Relevanter Hinweis: Wenn Sie zum Namespace der obersten Ebene zurückkehren möchten, gehen Sie folgendermaßen vor: :: SomeModule - Benjamin Oakes

Mipadi
quelle
5
In C # zum Beispiel ja. Auf der anderen Seite verwenden C ++ (und Ruby) ::für die Auflösung von Namespaces wiestd::cout << "Hello World!";
Jerry Fernholz
142
Relevanter Hinweis: Wenn Sie zum Namespace der obersten Ebene zurückkehren möchten, gehen Sie folgendermaßen vor: ::SomeModule
Benjamin Oakes
5
@Benjamin Die führenden Doppelpunkte sind impliziert, es sei denn, ich habe zufällig ein SomeModule in einem anderen Modul und möchte stattdessen das Top-Level-Modul erhalten, richtig?
Jo Liss
7
@ Jo Ja. Dies kann hilfreich sein, wenn Sie sicherstellen möchten, dass Sie sich auf eine Konstante im Namespace der obersten Ebene oder auf eine Konstante mit demselben Namen in einem anderen Modul beziehen (z. B. :: SomeOtherModule :: ClassMethods).
Benjamin Oakes
2
Dies ist sehr ähnlich wie Scope-Operand von C ++
lkahtz
111

Dieses einfache Beispiel veranschaulicht es:

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

Entnommen aus http://www.tutorialspoint.com/ruby/ruby_operators.htm

Nader
quelle
Dies ist jedoch der Grund für die Warnung. Gibt es eine Möglichkeit, der Warnung auszuweichen?
NullVoxPopuli
3
@NullVoxPopuli Das Ändern von Konstanten ist im Allgemeinen eine sehr schlechte Sache. Wenn Sie jedoch beispielsweise eine Konstante in einem schlecht geschriebenen Edelstein ändern und nicht verzweigen möchten, können Sie .send (: remove_const) für das definierende Modul verwenden es, dann die Konstante neu definieren.
BookOfGreg
71

::Ermöglicht den Zugriff auf eine Konstante, ein Modul oder eine Klasse, die in einer anderen Klasse oder einem anderen Modul definiert sind. Es wird verwendet, um Namespaces bereitzustellen, damit Methoden- und Klassennamen nicht mit anderen Klassen anderer Autoren in Konflikt stehen.

Wenn Sie ActiveRecord::Basein Rails sehen, bedeutet dies, dass Rails so etwas hat

module ActiveRecord
  class Base
  end
end

dh eine Klasse, die Basein einem Modul aufgerufen wird, auf ActiveRecorddas dann verwiesen wird ActiveRecord::Base(Sie finden dies in der Rails-Quelle unter activerecord-nnn / lib / active_record / base.rb).

Eine häufige Verwendung von :: ist der Zugriff auf Konstanten, die in Modulen definiert sind, z

module Math
  PI = 3.141 # ...
end

puts Math::PI

Der ::Operator erlaubt Ihnen nicht, die Sichtbarkeit von Methoden zu umgehen, die als privat oder geschützt markiert sind.

mikej
quelle
7
Wenn class MyClass < ActiveRecord::Baseja, bedeutet dies, dass MyClass nur Methoden von der Klassenbasis erbt und nichts im ActiveRecord-Modul?
Charlie Parker
2
Warum sollten Sie für diese Namespace-Auflösung den speziellen Doppelpunkt verwenden, anstatt das "." auch dafür? Kontext und Großschreibung würden eine Verwechslung der Bedeutung verhindern, selbst wenn wir das "." Verwenden würden, nicht wahr?
Jonah
3
@ Jonah Es gibt einige Fälle, in denen es mehrdeutig wäre. zB berücksichtigen class Foo; Baz = 42; def self.Baz; "Baz method!"; end; end(vollkommen gültig) Foo::Baz # => 42und Foo.Baz # => "Baz method!". Beachten Sie, dass Foo::Baz()(mit Klammern) die Methode jedoch auch aufgerufen wird.
Mikej
3
Der Anwendungsfall löst also die Möglichkeit, eine Klassenkonstante und eine Klassenmethode mit genau demselben Namen zu haben? Das scheint kein starkes Argument für das Feature zu sein. Persönlich würde ich diese Fähigkeit viel lieber verlieren (scheint sowieso Ärger zu sein), den Doppelpunkt verlieren und "." auch für den Namespace .... Vielleicht gibt es zusätzliche Anwendungsfälle, die es löst?
Jonah
26

Was nützt der Bereich (privat, geschützt), wenn Sie einfach :: verwenden können, um etwas freizulegen?

In Ruby wird alles angezeigt und alles kann von jedem anderen Ort aus geändert werden.

Wenn Sie sich Sorgen darüber machen, dass Klassen von außerhalb der "Klassendefinition" geändert werden können, ist Ruby wahrscheinlich nichts für Sie.

Wenn Sie dagegen frustriert sind, dass Javas Klassen gesperrt sind, ist Ruby wahrscheinlich das, wonach Sie suchen.

Yfeldblum
quelle
1
Ich habe einige Rubinisten sagen hören, dass Instanzvariablen nicht verfügbar gemacht werden, sondern sogar attr_accessornur Methoden, die die Variable ändern. (Dann gibt es wieder instance_eval)
Andrew Grimm
4
Richtig, da ist instance_eval. Aber es gibt auch instance_variable_getund instance_variable_set. Ruby ist einfach zu dynamisch für Einschränkungen.
Yfeldblum
12

Zusätzlich zu früheren Antworten ist Ruby gültig, um ::auf Instanzmethoden zuzugreifen. Alle folgenden sind gültig:

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

Nach den Best Practices wird meines Erachtens nur der letzte empfohlen.

Juri Ghensev
quelle
11

Nein, es ist nicht auf jede Methode zuzugreifen, es ist ein "Auflösungs" -Operator, dh Sie verwenden ihn, um den Umfang (oder die Position, die Sie sagen können) eines konstanten / statischen Symbols aufzulösen.

In der ersten Zeile verwenden Rails die Basisklasse im ActiveRecord.Module. In der zweiten Zeile wird die Klassenmethode (statisch) der Routes-Klasse usw. usw. gesucht.

Es wird nicht verwendet, um etwas freizulegen, es wird verwendet, um Dinge in Ihrer Nähe zu "lokalisieren".

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

Francisco Soto
quelle
mit "(statisch)" meinst du "(zeichnen)"?!?
Meltemi
8

Überraschenderweise sagen alle 10 Antworten hier dasselbe. Das '::' ist ein Namespace-Auflösungsoperator, und ja, es ist wahr. Es gibt jedoch eine Sache, die Sie über den Namespace-Auflösungsoperator wissen müssen, wenn es um den Algorithmus für die konstante Suche geht . Wie Matz in seinem Buch "The Ruby Programming Language" beschreibt, besteht die ständige Suche aus mehreren Schritten. Zunächst wird eine Konstante im lexikalischen Bereich durchsucht, auf die auf die Konstante verwiesen wird. Wenn die Konstante nicht im lexikalischen Bereich gefunden wird, wird die Vererbungshierarchie durchsucht . Aufgrund dieses konstanten Suchalgorithmus erhalten wir unten die erwarteten Ergebnisse:

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

Während F von E erbt, befindet sich das B-Modul im lexikalischen Bereich von F. Folglich beziehen sich F-Instanzen auf die im Modul B definierte Konstante PI. Wenn Modul B PI nicht definiert hat, beziehen sich F-Instanzen auf den PI Konstante in der Oberklasse E definiert.

Aber was wäre, wenn wir '::' verwenden würden, anstatt Module zu verschachteln? Würden wir das gleiche Ergebnis erzielen? Nein!

Durch die Verwendung des Namespace-Auflösungsoperators beim Definieren verschachtelter Module befinden sich die verschachtelten Module und Klassen nicht mehr im lexikalischen Bereich ihrer äußeren Module. Wie Sie unten sehen können, befindet sich das in A :: B definierte PI nicht im lexikalischen Bereich von A :: B :: C :: D, und daher erhalten wir eine nicht initialisierte Konstante, wenn wir versuchen, in der Instanzmethode get_pi auf PI zu verweisen:

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
Donato
quelle
4

Es geht darum zu verhindern, dass Definitionen mit anderem Code in Konflikt geraten, der mit Ihrem Projekt verknüpft ist. Es bedeutet, dass Sie die Dinge getrennt halten können.

Zum Beispiel können Sie eine Methode namens "run" in Ihrem Code haben und Sie können weiterhin Ihre Methode aufrufen, anstatt die "run" -Methode, die in einer anderen Bibliothek definiert wurde, in der Sie verlinkt haben.

Mongus Pong
quelle
3
module Amimal
      module Herbivorous
            EATER="plants" 
      end
end

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

:: Wird zum Erstellen eines Bereichs verwendet. Um von 2 Modulen aus auf Constant EATER zugreifen zu können, müssen die Module so bemessen sein, dass sie die Konstante erreichen

Francesca Rodricks
quelle
3

Ruby on Rails wird ::für die Namespace-Auflösung verwendet.

class User < ActiveRecord::Base

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

end

Um es zu benutzen :

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

Eine andere Verwendung ist: Bei Verwendung verschachtelter Routen

OmniauthCallbacksController wird unter Benutzer definiert.

Und geroutet als:

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


class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

end
Pankhuri
quelle