В чем разница между `try` и` &.` (безопасным навигационным оператором) в Ruby
Вот мой код:
class Order < Grape::Entity
expose :id { |order, options| order.id.obfuscate }
expose :time_left_to_review do |order, options|
byebug
order&.time_left_to_review # ERROR
end
expose :created_at { |order, options| order.last_transition.created_at }
end
# NoMethodError Exception: undefined method `time_left_to_review' for #<Order:0x007f83b9efc970>
Я думал, что &.
является ярлыком для .try
, но я думаю, что я ошибся. Может кто-нибудь указать мне в правильном направлении относительно того, чего я не хватает?
Мне кажется, что это не рубин. Виноград может быть? Хотя я не понимаю, как это может быть.
Ответы
Ответ 1
&.
работает как #try!
, а не #try
.
А вот и описание #try!
(из документации):
То же, что и #try, но вызовет исключение NoMethodError, если получатель не равен nil и не реализует проверенный метод.
Таким образом, в основном это избавляет вас от вызова метода для nil
, но если объект представлен, он попытается вызвать его метод как обычно.
Цитата #try
из документации Rails, поэтому важно подчеркнуть, что Ruby не предоставляет #try
; это обеспечивается Rails, или, точнее, ActiveSupport. Оператор безопасной навигации (&.
), Однако, является языковой функцией, представленной в Ruby 2.3.0.
Ответ 2
Метод try
игнорирует много вещей, он просто дает ему шанс и называет его днем, если что-то не получается.
Параметр условной навигации &
будет блокировать вызовы только для объектов nil
. Все остальное считается действительным и будет действовать с полными последствиями, включая исключения.
Ответ 3
согласен с @Redithion в комментарии
Оператор безопасной навигации (&.) В Ruby
сценарий
Представьте, что у вас есть account
которой есть owner
и вы хотите получить address
владельца. Если вы хотите быть в безопасности и не рисковать ошибкой Nil, напишите что-то вроде следующего.
if account && account.owner && account.owner.address
...
end
Это действительно многословно и раздражает печатать. ActiveSupport включает в себя метод try
который имеет похожее поведение (но с несколькими ключевыми отличиями, которые будут обсуждаться позже):
if account.try(:owner).try(:address)
...
end
Он выполняет то же самое - возвращает либо адрес, либо nil
если какое-то значение в цепочке равно nil
. В первом примере также может возвращаться значение false, если, например, для owner
установлено значение false.
С помощью &.
Мы можем переписать предыдущий пример, используя оператор безопасной навигации:
account&.owner&.address
Больше примеров
Давайте сравним все три подхода более подробно.
account = Account.new(owner: nil) # account without an owner
account.owner.address
# => NoMethodError: undefined method 'address' for nil:NilClass
account && account.owner && account.owner.address
# => nil
account.try(:owner).try(:address)
# => nil
account&.owner&.address
# => nil
Пока никаких сюрпризов. Что если owner
false
(вряд ли, но не невозможный в захватывающем мире дерьмового кода)?
account = Account.new(owner: false)
account.owner.address
# => NoMethodError: undefined method 'address' for false:FalseClass '
account && account.owner && account.owner.address
# => false
account.try(:owner).try(:address)
# => nil
account&.owner&.address
# => undefined method 'address' for false:FalseClass'
Здесь приходит первый сюрприз - &.
синтаксис пропускает только nil
но распознает ложь! Это не совсем эквивалентно синтаксису s1 && s1.s2 && s1.s2.s3
.
Что если владелец присутствует, но не отвечает на address
?
account = Account.new(owner: Object.new)
account.owner.address
# => NoMethodError: undefined method 'address' for #<Object:0x00559996b5bde8>
account && account.owner && account.owner.address
# => NoMethodError: undefined method 'address' for #<Object:0x00559996b5bde8>'
account.try(:owner).try(:address)
# => nil
account&.owner&.address
# => NoMethodError: undefined method 'address' for #<Object:0x00559996b5bde8>'
пример ниже сбивает с толку и nil&.nil?
должен вернуть true
.
Будьте осторожны при использовании &.
оператор и проверка на nil
значения. Рассмотрим следующий пример:
nil.nil?
# => true
nil?.nil?
# => false
nil&.nil?
# => nil
Ответ 4
Я приезжаю на вечеринку немного позже, другие ответы касаются того, как это работает, но я хотел добавить что-то, что другие ответы не были затронуты.
Ваш вопрос спрашивает, в чем разница между try
и &.
в Ruby. Здесь ключевое слово Ruby.
Самое большое отличие состоит в том, что try
не существует в Ruby, это метод, предоставляемый Rails. вы можете увидеть это или себя, если вы сделаете что-то подобное в консоли rails:
[1, 2, 3].try(:join, '-')
#=> "1-2-3"
Однако, если вы сделаете то же самое в консоли irb, вы получите:
[1, 2, 3].try(:join, '-')
NoMethodError: undefined method `try' for [1, 2, 3]:Array
&.
является частью стандартной библиотеки Ruby и поэтому доступен в любом проекте Ruby, а не только в Rails.
Ответ 5
согласен с @Redithion в разделе комментариев
Оператор безопасной навигации (&.) В Ruby
Оператор безопасной навигации возвращает nil, если метод вызывается для объекта nil. Перед этим, если метод вызывается для объекта nil, он выдает ошибку, как указано ниже.
$ nil.some_method
=> NoMethodError: undefined method 'some_method' for nil:NilClass
Зачем нам нужна безопасная навигация?
Много раз мы не получаем объект из-за неправильных входных значений. В этом случае, если мы продолжим вызывать методы, ожидающие, поскольку у нас есть объект, код может прерваться, если объект окажется нулевым объектом.
Чтобы избежать такого случая, введена безопасная навигация. Это гарантирует, что наш код не сломается, даже если объект, для которого вызывается метод, является нулевым объектом. Это следует использовать, когда мы хорошо получаем объект nil, когда вызов метода завершается неудачно.
Примеры
Представьте, что у вас есть учетная запись, у которой есть владелец, и вы хотите получить адрес владельца. Если вы хотите быть в безопасности и не рисковать ошибкой Nil, напишите что-то вроде следующего.
if account && account.owner && account.owner.address
...
end
Это действительно многословно и раздражает печатать. ActiveSupport включает в себя метод try, который имеет похожее поведение (но с несколькими ключевыми отличиями, которые будут обсуждаться позже):
if account.try(:owner).try(:address)
...
end
Он выполняет то же самое - возвращает либо адрес, либо ноль, если какое-либо значение в цепочке равно нулю. В первом примере также может возвращаться значение false, если, например, для владельца установлено значение false.
С помощью &.
Мы можем переписать предыдущий пример, используя оператор безопасной навигации:
account&.owner&.address