Co robi &. (znak ampersand dot) oznacza w języku Ruby?

Odpowiedzi:

205

Nazywa się Operator Bezpiecznej Nawigacji. Wprowadzony w Ruby 2.3.0, pozwala na wywoływanie metod na obiektach bez obawy, że obiekt może być nil(Unikanie undefined method for nil:NilClassbłędu), podobnie jak trymetoda w Railsach .

Więc możesz pisać

@person&.spouse&.name

zamiast

@person.spouse.name if @person && @person.spouse

Z Dokumentów :

my_object.my_method

Spowoduje to wysłanie wiadomości my_method do my_object. Każdy obiekt może być odbiornikiem, ale w zależności od widoczności metody wysłanie wiadomości może zgłosić NoMethodError.

Możesz użyć &. aby wyznaczyć odbiorcę, wówczas my_method nie jest wywoływana, a wynik jest zerowy, gdy odbiornik jest zerowy. W takim przypadku argumenty my_method nie są oceniane.

Santhosh
źródło
13
właściwie to działa jak try!metoda, a nietry
Grzegorz
58

Uwaga: Mimo że @Santosh udzielił jasnej i pełnej odpowiedzi, chciałbym dodać więcej tła i dodać ważną uwagę dotyczącą jego użycia ze zmiennymi innymi niż instancje.


Nazywa się to „ Bezpiecznym operatorem nawigacji ” ( inaczej „Opcjonalny operator łańcuchowy”, „Operator warunkowy zerowy” itp .). Matz zdaje się nazywać to „samotnym operatorem”. Został wprowadzony w Rubim 2.3 . Wysyła metodę do obiektu tylko wtedy, gdy tak nie jest nil.

Przykład:

# Call method `.profile` on `user` only if `user` is not `nil`
@user&.profile

# Equivalent to
unless @user.nil?
  @user.profile
end

„Przypadek skrajny” ze zmiennymi lokalnymi:

Zwróć uwagę, że powyższy kod używa zmiennych instancji. Jeśli chcesz używać bezpiecznego operatora nawigacji ze zmiennymi lokalnymi, musisz najpierw sprawdzić, czy zmienne lokalne są zdefiniowane.

# `user` local variable is not defined previous
user&.profile

# This code would throw the following error:
NameError: undefined local variable or method `user' for main:Object

Aby rozwiązać ten problem, sprawdź, czy zmienna lokalna jest zdefiniowana jako pierwsza, lub ustaw ją na zero:

# Option 1: Check the variable is defined
if defined?(user)
  user&.profile
end

# Option 2: Define your local variable. Example, set it to nil
user = nil
user&.profile     # Works and does not throw any errors

Tło metody

Railsy mają trymetodę, która w zasadzie robi to samo. Używa sendmetody wewnętrznie do wywołania metody. Matz zasugerował, że jest to powolne i powinno to być wbudowaną funkcją języka.

Wiele innych języków programowania ma podobną funkcję: Objective C, Swift, Python, Scala, CoffeeScript itp. Jednak typową składnią jest ?.(kropka zapytania). Ale Ruby nie mógł przyjąć tej składni. Ponieważ ?było to dozwolone w nazwach metod, ?.sekwencja symboli jest już poprawnym kodem Rubiego. Na przykład:

2.even?.class  # => TrueClass

Dlatego społeczność Rubiego musiała wymyślić inną składnię. To była aktywna dyskusja i rozważono różne warianty ( .?, ?, &&, itd.). Oto lista kilku uwag:

u.?profile.?thumbnails
u\profile\thumbnails
u!profile!thumbnails
u ? .profile ? .thumbnails
u && .profile && .thumbnails

# And finally
u&.profile&.thumbnails

Wybierając składnię, programiści przyjrzeli się różnym skrajnym przypadkom i dyskusja jest bardzo przydatna. Jeśli chcesz przejrzeć wszystkie warianty i niuanse operatora, zapoznaj się z dyskusją wprowadzającą do funkcji dotyczącą oficjalnego narzędzia do śledzenia problemów w Rubim.

Uzbekjon
źródło
Co jest '.?' ? Myślę, że zdecydowano, że ta składnia nie będzie możliwa do użycia. Mogę uzyskać tylko „&”. pracować.
Keith Bennett
2
Dokładnie tak, jak napisałem .?i rozważono inne opcje, ale &.wybrano! Więc tylko &.zadziała.
Uzbekjon
Och, przepraszam, nie przeczytałem dokładnie do końca. Czy nie byłoby lepiej, gdyby przykłady miały poprawną składnię?
Keith Bennett
@KeithBennett Rozumiem, co masz na myśli. W pierwszym przykładzie miałem literówkę. Masz rację, to musi być, &.a nie .?, moja wina. Zaktualizowałem moją odpowiedź. Dzięki za ostrzeżenie!
Uzbekjon
@Uzbekjon W przykładzie Edge'a wciąż jest literówka (opcja 1): user.?profilepowinno być user&.profile(nie mogłem tego samodzielnie edytować, ponieważ jest to edycja 1 znaku!).
Yanis Vieilly
7

Uważać! Chociaż bezpieczny operator nawigacji jest wygodny, można też łatwo oszukać siebie i zmienić za jego pomocą logikę. Zalecam unikanie używania tego w kontroli przepływu. Przykład:

str = nil

puts "Hello" if str.nil? || str.empty?
# The above line is different than the below line
puts "Hello" if str&.empty?

W pierwszym przykładzie str.nil?zwraca truei str.empty?nigdy nie jest wywoływana, powodując wykonanie putsinstrukcji. Jednak w drugim przykładzie str&.empty?zwraca wynik, nilktóry jest fałszywy, a putsinstrukcja nigdy nie jest wykonywana.

Thomas Deranek
źródło
Ciekawe, chociaż technicznie rzecz biorąc w pierwszym stwierdzeniu str.nil?jest prawda (jak mówisz), co oznacza, str.empty?że w rzeczywistości nie zostanie w ogóle wywołany (tak ||działa operator). Reszta twojego stwierdzenia jest poprawna.
Ollie Bennett
1
Och, dobry chwyt. Nie jestem pewien, jak to przegapiłem. Poprawię mój post. Dziękuję Ci!
Thomas Deranek
2
co prawda dużo łatwiej jest użyć złej logiki, ale sprawdzasz tutaj różne rzeczy. druga linia jest odpowiednikiem if str && str.empty? nie zero || pusty. operator bezpiecznej nawigacji nadal doskonale nadaje się do sterowania przepływem puts "hello" unless str&.present?(szyny tylko dla obecnej metody?)
Sampson Crowley
1

używany do sprawdzenia zerowego, na przykład w kotlin i swift; z Object -> Swift i Kotlin

model = car?.model

ten model może być zerowy (Swift) lub zerowy (Kotlin), jeśli nie zdefiniowaliśmy wartości modelu w klasie samochodu. używamy tego ampersandu zamiast znaku zapytania w rubinie

model = car&.model

jeśli używasz car.model bez znaku ampersand i jeśli model jest zerowy, system nie może kontynuować pracy.

Umut ADALI
źródło