Що робить &. (ampersand dot) означає в Ruby?


Відповіді:


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 не оцінюються.


13
насправді він працює як try!метод, а неtry
Гжегож

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

Щоб виправити цю проблему, перевірте, чи локальна змінна визначена спочатку або встановіть її на нуль:

# 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. Наприклад:

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.


Що '.?' ? Я думаю, було вирішено, що цей синтаксис використовувати не вдасться. Я можу отримати лише "&". працювати.
Кіт Беннетт

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?повертається nilфальсифікат, а putsзаява ніколи не виконується.


Цікаво, хоча технічно у вашому першому твердженні str.nil?є правдивим (як ви кажете), що означає str.empty?, що насправді взагалі не буде викликано (саме так ||працює оператор). Решта ваших тверджень правильна.
Оллі Беннетт

1
О гарний улов. Я не впевнений, як я пропустив це. Я виправлю свою посаду. Дякую!
Томас Деранек

2
правда, набагато простіше використовувати неправильну логіку, але ви перевіряєте тут різні речі. другий рядок еквівалентний, якщо str && str.empty? не нульовий || порожній. оператора безпечної навігації все ще цілком справедливо використовувати для контролю потоку puts "hello" unless str&.present?(рейки тільки для теперішнього методу)
Sampson Crowley

1

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

model = car?.model

ця модель може бути нульовою (Swift) або null (Kotlin), якщо ми не визначили значення моделі в автомобільному класі. ми використовуємо цей символ замість знака питання в рубіні

model = car&.model

якщо використовувати car.model без ampersand і якщо модель є нульовою, система не може продовжувати працювати.

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