Que signifie &. (esperluette) signifie en Ruby?

Réponses:

205

Il est appelé l'opérateur de navigation sécurisée. Introduit dans Ruby 2.3.0, il vous permet d'appeler des méthodes sur des objets sans vous soucier que l'objet puisse être nil(Éviter une undefined method for nil:NilClasserreur), similaire à la tryméthode de Rails .

Pour que tu puisses écrire

@person&.spouse&.name

au lieu de

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

À partir des documents :

my_object.my_method

Cela envoie le message my_method à my_object. Tout objet peut être un récepteur, mais selon la visibilité de la méthode, l'envoi d'un message peut déclencher une NoMethodError.

Vous pouvez utiliser &. pour désigner un receveur, alors my_method n'est pas invoqué et le résultat est nul lorsque le receveur est nul. Dans ce cas, les arguments de my_method ne sont pas évalués.

Santhosh
la source
13
en fait, cela fonctionne comme la try!méthode, pastry
Grzegorz
58

Remarque: Même si @Santosh a donné une réponse claire et complète, je voudrais ajouter un peu plus de contexte et ajouter une note importante concernant son utilisation avec des variables non-instance.


Il est appelé « opérateur de navigation sécurisée » ( alias «opérateur de chaînage facultatif», «opérateur conditionnel nul», etc. ). Matz semble l'appeler "opérateur solitaire". Il a été introduit dans Ruby 2.3 . Il envoie une méthode à un objet uniquement si ce n'est pas le cas nil.

Exemple:

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

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

"Edge case" avec des variables locales:

Veuillez noter que le code ci-dessus utilise des variables d'instance. Si vous souhaitez utiliser un opérateur de navigation sécurisé avec des variables locales, vous devrez d'abord vérifier que vos variables locales sont définies.

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

Pour résoudre ce problème, vérifiez si votre variable locale est définie en premier ou définissez-la sur 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

Contexte de la méthode

Rails a une tryméthode qui fait essentiellement la même chose. Il utilise une sendméthode en interne pour appeler une méthode. Matz a suggéré qu'il était lent et que cela devrait être une fonctionnalité de langage intégrée.

De nombreux autres langages de programmation ont des fonctionnalités similaires: Objective C, Swift, Python, Scala, CoffeeScript, etc. Cependant, une syntaxe commune est ?.(point de question). Mais, cette syntaxe n'a pas pu être adoptée par Ruby. Parce que cela ?était autorisé dans les noms de méthode et donc, la ?.séquence de symboles est déjà un code Ruby valide. Par exemple:

2.even?.class  # => TrueClass

C'est pourquoi la communauté Ruby a dû proposer une syntaxe différente. Ce fut une discussion active et différentes options ont été envisagées ( .?, ?, &&, etc.). Voici une liste de quelques considérations:

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

# And finally
u&.profile&.thumbnails

Lors du choix de la syntaxe, les développeurs ont examiné différents cas limites et la discussion est assez utile à parcourir. Si vous souhaitez parcourir toutes les variantes et nuances de l'opérateur, veuillez consulter cette discussion d'introduction des fonctionnalités sur le suivi des problèmes Ruby officiel.

Ouzbekjon
la source
Quel est '.?' ? Je pense qu'il a été décidé que cette syntaxe ne serait pas possible à utiliser. Je ne peux obtenir que «&». travailler.
Keith Bennett
2
Exactement, comme je l'ai écrit .?et d'autres options ont été envisagées, mais elles ont &.été choisies! Donc, seul &.fonctionnera.
Ouzbekjon
Oh, désolé, je n'ai pas lu complètement jusqu'à la fin. Ne serait-il pas préférable que les exemples aient la syntaxe correcte?
Keith Bennett
@KeithBennett Je vois ce que tu veux dire. J'ai eu une faute de frappe dans mon premier exemple. Tu as raison, ça doit être &.et non .?, mon mal. J'ai mis à jour ma réponse. Merci pour l'information!
Ouzbekjon
@Uzbekjon Il y a toujours une faute de frappe dans l' exemple de cas Edge (option 1): user.?profiledevrait être user&.profile(je n'ai pas pu le modifier moi-même car il ne s'agit que d'une modification d'un caractère!).
Yanis Vieilly
7

Se méfier! Bien que l'opérateur de navigation sécurisé soit pratique, il peut également être facile de vous inciter à changer votre logique avec lui. Je recommande d'éviter son utilisation dans le contrôle de flux. Exemple:

str = nil

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

Dans le premier exemple, str.nil? retourne trueet str.empty?n'est jamais appelé, ce qui entraîne l' putsexécution de l' instruction. Dans le deuxième exemple cependant, str&.empty?renvoie nilce qui est faux et l' putsinstruction n'est jamais exécutée.

Thomas Deranek
la source
Intéressant, bien que techniquement dans votre première déclaration, str.nil?c'est vrai (comme vous le dites), ce qui signifie str.empty?ne sera pas du tout appelé (c'est ainsi que fonctionne l' ||opérateur). Le reste de votre déclaration est correct.
Ollie Bennett
1
Oh bonne prise. Je ne sais pas comment j'ai raté ça. Je vais corriger mon message. Je vous remercie!
Thomas Deranek
2
il est vrai qu'il est beaucoup plus facile d'utiliser la mauvaise logique, mais vous vérifiez différentes choses ici. la deuxième ligne équivaut à if str && str.empty? pas nul || vide. l'opérateur de navigation en toute sécurité est toujours parfaitement valable à utiliser pour le contrôle de flux puts "hello" unless str&.present?(rails uniquement pour la méthode actuelle?)
Sampson Crowley
1

il a utilisé pour un contrôle nul, comme dans kotlin et swift Par exemple; avec Object -> Swift et Kotlin

model = car?.model

ce modèle peut être nul (Swift) ou nul (Kotlin) si nous n'avons pas défini la valeur du modèle dans la classe de voiture. nous utilisons cette esperluette au lieu du point d'interrogation en rubis

model = car&.model

si vous utilisez car.model sans esperluette et si le modèle est nul, le système ne peut pas continuer à fonctionner.

Umut ADALI
la source