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

Ответы:

205

Это называется оператор безопасной навигации. Представленный в 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 не вызывается, и результат равен нулю, когда получатель равен нулю. В этом случае аргументы my_method не оцениваются.

Santhosh
источник
13
на самом деле, это работает как try!метод, а неtry
Grzegorz
58

Примечание. Несмотря на то, что @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.

Uzbekjon
источник
Что '.?' ? Я думаю, что было решено, что этот синтаксис будет невозможно использовать. Я могу получить только '&.' работать.
Кит Беннетт
2
Точно, как я и писал .?, были рассмотрены и другие варианты, но &.был выбран! Так что, только &.сработает.
Узбекджон
Ой, прости, я не читал до конца. Разве не было бы лучше, если бы примеры имели правильный синтаксис?
Кит Беннетт
@KeithBennett Я понимаю, что ты имеешь в виду. У меня была опечатка в моем первом примере. Вы правы, должно быть &.и нет .?, мой плохой. Обновил мой ответ. Спасибо за головы!
Узбекджон
@Uzbekjon В примере с Edge все еще есть опечатка (вариант 1): user.?profileдолжно быть user&.profile(не могу редактировать сам, так как это всего лишь 1 символ!).
Янис Вьейли
7

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

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?возвращается значение nilfalse, и putsоператор никогда не выполняется.

Томас Деранек
источник
Интересно, хотя технически ваше первое утверждение str.nil?верно (как вы говорите), что означает, str.empty?что на самом деле вообще не будет вызываться (так ||работает оператор). Остальная часть вашего утверждения верна.
Олли Беннетт
1
О, хороший улов. Я не уверен, как я это пропустил. Я исправлю свой пост. Спасибо!
Томас Деранек
2
Это правда, что гораздо проще использовать неправильную логику, но вы здесь проверяете разные вещи. вторая строка эквивалентна, если str && str.empty? не ноль || опорожнить. оператор безопасной навигации все еще идеально подходит для управления потоком puts "hello" unless str&.present?(рельсы только для настоящего метода?)
Сэмпсон Кроули,
1

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

model = car?.model

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

model = car&.model

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

Умут АДАЛИ
источник