Что значит &. (точка амперсанда) означает в Ruby?

Я наткнулся на эту строку рубинового кода. Что здесь означает &.?

@object&.method

person Sami    schedule 23.04.2016    source источник


Ответы (4)


Он называется «Оператор безопасной навигации». Представленный в Ruby 2.3.0, он позволяет вам вызывать методы для объектов, не беспокоясь о том, что объект может быть nil (избегая ошибки undefined method for nil:NilClass), подобно try в Rails.

Итак, вы можете написать

@person&.spouse&.name

вместо

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

Из документов:

my_object.my_method

Это отправляет сообщение my_method на my_object. Любой объект может быть получателем, но в зависимости от видимости метода отправка сообщения может вызвать NoMethodError.

Вы можете использовать &. для обозначения получателя, тогда my_method не вызывается и результатом будет nil, когда получателем является nil. В этом случае аргументы my_method не оцениваются.

person Santhosh    schedule 23.04.2016
comment
на самом деле он работает как метод try!, а не try - person Grzegorz; 13.12.2016

Примечание. Несмотря на то, что @Santosh дал четкий и полный ответ, я хотел бы добавить дополнительную информацию и добавить важное примечание относительно его использования с переменными, не являющимися экземплярами.


Он называется "Оператор безопасной навигации" (он же "Необязательный оператор цепочки", "Нулевой -условный оператор" и т.д.). Мац, кажется, называет это «одиноким оператором». Он был введен в Ruby 2.3. Он отправляет метод объекту, только если он не nil.

Пример:

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

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

«Пограничный случай» с локальными переменными:

Обратите внимание, что в приведенном выше коде используются переменные экземпляра. Если вы хотите использовать безопасный оператор навигации с локальными переменными, вам нужно сначала убедиться, что ваши локальные переменные определены.

# `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

Чтобы решить эту проблему, сначала проверьте, определена ли ваша локальная переменная, или установите для нее значение nil:

# 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

Предыстория метода

В Rails есть метод try, который в основном делает то же самое. Он использует метод send внутри для вызова метода. предложен Матц что он медленный и это должно быть встроенной функцией языка.

Многие другие языки программирования имеют аналогичную функцию: Objective C, Swift, Python, Scala, CoffeeScript и т. д. Однако общий синтаксис — ?. (вопросительная точка). Но этот синтаксис не мог быть принят Ruby. Поскольку ? было разрешено в именах методов и, таким образом, последовательность символов ?. уже является допустимым кодом Ruby. Например:

2.even?.class  # => TrueClass

Вот почему сообществу Ruby пришлось придумать другой синтаксис. Было активное обсуждение и рассматривались разные варианты (.?, ?, && и т.д.). Вот список некоторых соображений:

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

# And finally
u&.profile&.thumbnails

При выборе синтаксиса разработчики учитывали различные пограничные случаи, и обсуждение весьма полезно. Если вы хотите ознакомиться со всеми вариантами и нюансами оператора, см. введение в эту функцию на официальном трекере проблем Ruby.

person Uzbekjon    schedule 23.04.2016
comment
Что '.?' ? Я думаю, было решено, что этот синтаксис нельзя будет использовать. Я могу получить только '&.' работать. - person Keith Bennett; 23.04.2016
comment
Именно так, как я и писал, рассматривались .? и другие варианты, но был выбран &.! Итак, только &. будет работать. - person Uzbekjon; 23.04.2016
comment
Ой, извините, не дочитала до конца. Не лучше ли было бы, если бы примеры имели правильный синтаксис? - person Keith Bennett; 23.04.2016
comment
@KeithBennett Я понимаю, что ты имеешь в виду. У меня была опечатка в моем первом примере. Вы правы, это должно быть &., а не .?, мой плохой. Обновил мой ответ. Спасибо за внимание! - person Uzbekjon; 24.04.2016
comment
@Uzbekjon В примере Edge case все еще есть опечатка (вариант 1): user.?profile должно быть user&.profile (сам не смог отредактировать, так как это всего лишь редактирование 1 символа!). - person Yanis Vieilly; 30.08.2016
comment
Пожалуйста, перестаньте сравнивать &. с #try ActiveSupport. Они делают разные вещи, и попытка объяснить оператор безопасной навигации с точки зрения #try скорее сбивает с толку, чем помогает. :-) - person Drenmi; 02.01.2017

Будьте осторожны! Хотя оператор безопасной навигации удобен, с его помощью также легко обмануть себя, заставив изменить свою логику. Я рекомендую избегать его использования в управлении потоком. Пример:

str = nil

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

В первом примере str.nil? возвращает true, а str.empty? никогда не вызывается, что приводит к выполнению оператора puts. Однако во втором примере str&.empty? возвращает nil, что является ложным, а оператор puts никогда не выполняется.

person Thomas Deranek    schedule 07.02.2018
comment
Интересно, хотя технически в вашем первом утверждении str.nil? верно (как вы говорите), что означает, что str.empty? на самом деле вообще не будет вызываться (так работает оператор ||). В остальном ваше утверждение верно. - person Ollie Bennett; 27.06.2018
comment
О, хороший улов. Не знаю, как я это пропустил. Я поправлю свой пост. Спасибо! - person Thomas Deranek; 29.06.2018
comment
это правда, что гораздо проще использовать неправильную логику, но вы проверяете здесь разные вещи. вторая строка эквивалентна if str && str.empty? не ноль || пустой. оператор безопасной навигации по-прежнему можно использовать для управления потоком puts "hello" unless str&.present? (метод rails только для настоящего?) - person Sampson Crowley; 06.11.2018

он используется для нулевой проверки, например, в kotlin и swift Например; с Объектом -> Свифт и Котлин

model = car?.model

эта модель может быть нулевой (Swift) или нулевой (Kotlin), если мы не определили значение модели в классе автомобиля. мы используем этот амперсанд вместо вопросительного знака в ruby

model = car&.model

если использовать car.model без амперсанда и если модель равна нулю, система не может продолжать работу.

person Umut ADALI    schedule 21.01.2019