Cosa fa &. (punto e commerciale) significa in Ruby?


Risposte:


205

Si chiama Safe Navigation Operator. Introdotto in Ruby 2.3.0, ti consente di chiamare metodi su oggetti senza preoccuparti che l'oggetto possa essere nil(Evitare un undefined method for nil:NilClasserrore), simile al trymetodo in Rails .

Quindi puoi scrivere

@person&.spouse&.name

invece di

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

Dai documenti :

my_object.my_method

Questo invia il messaggio my_method a my_object. Qualsiasi oggetto può essere un destinatario ma, a seconda della visibilità del metodo, l'invio di un messaggio può generare un NoMethodError.

Puoi usare &. per designare un ricevitore, quindi my_method non viene invocato e il risultato è zero quando il ricevitore è zero. In tal caso, gli argomenti di my_method non vengono valutati.


13
in realtà, funziona come il try!metodo, nontry
Grzegorz,

58

Nota: anche se @Santosh ha dato una risposta chiara e completa, vorrei aggiungere un po 'più di background e aggiungere una nota importante relativa al suo utilizzo con variabili non di istanza.


Si chiama " Operatore di navigazione sicura " ( noto anche come "Operatore di concatenamento opzionale", "Operatore nullo-condizionato", ecc .). Matz sembra chiamarlo "operatore solitario". È stato introdotto in Ruby 2.3 . Invia un metodo a un oggetto solo se non lo è nil.

Esempio:

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

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

"Edge case" con variabili locali:

Nota: il codice sopra utilizza le variabili di istanza. Se si desidera utilizzare un operatore di navigazione sicura con variabili locali, è necessario verificare prima che le variabili locali siano definite.

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

Per risolvere questo problema, controlla se la variabile locale è definita per prima o impostala su zero:

# 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

Sfondo del metodo

Rails ha un trymetodo che sostanzialmente fa lo stesso. Usa il sendmetodo internamente per chiamare un metodo. Matz ha suggerito che è lento e questa dovrebbe essere una funzionalità linguistica integrata.

Molti altri linguaggi di programmazione hanno funzionalità simili: Obiettivo C, Swift, Python, Scala, CoffeeScript, ecc. Tuttavia, una sintassi comune è ?.(punto interrogativo). Ma questa sintassi non può essere adottata da Ruby. Perché ?era consentito nei nomi dei metodi e quindi, la ?.sequenza di simboli è già un codice Ruby valido. Per esempio:

2.even?.class  # => TrueClass

Ecco perché la comunità di Ruby ha dovuto elaborare una sintassi diversa. E 'stata una discussione attiva e diverse opzioni sono stati considerati ( .?, ?, &&, etc.). Ecco un elenco di alcune considerazioni:

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

# And finally
u&.profile&.thumbnails

Mentre scelgono la sintassi, gli sviluppatori hanno esaminato diversi casi limite e la discussione è abbastanza utile da affrontare. Se vuoi esaminare tutte le varianti e le sfumature dell'operatore, consulta questa discussione di introduzione della funzione sul tracker dei problemi di Ruby ufficiale.


Cosa è '.?' ? Penso che sia stato deciso che questa sintassi non sarebbe stata possibile utilizzare. Posso solo ottenere "&". lavorare.
Keith Bennett,

2
Esattamente, come ho scritto .?e sono state prese in considerazione altre opzioni, ma è &.stata scelta! Quindi, funzionerà solo &..
Uzbekjon,

Oh, scusa, non ho letto bene fino alla fine. Non sarebbe meglio se gli esempi avessero la sintassi corretta però?
Keith Bennett,

@KeithBennett Capisco cosa intendi. Ho avuto un refuso nel mio primo esempio. Hai ragione, deve essere &.e non .?, mia cattiva. Aggiornato la mia risposta. Grazie per il testa a testa!
Uzbekjon,

@Uzbekjon C'è ancora un refuso nell'esempio del caso Edge (opzione 1): user.?profiledovrebbe essere user&.profile(impossibile modificarlo da solo poiché è solo una modifica di 1 carattere!).
Yanis Vieilly,

7

Diffidare! Sebbene l'operatore di navigazione sicura sia conveniente, può anche essere facile indurti a cambiare la logica con esso. Raccomando di evitarne l'uso nel controllo del flusso. Esempio:

str = nil

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

Nel primo esempio, str.nil?restituisce truee str.empty?non viene mai chiamato, causando l' putsesecuzione dell'istruzione. Nel secondo esempio, tuttavia, str&.empty?restituisce nilche è falso e l' putsaffermazione non viene mai eseguita.


Interessante, anche se tecnicamente nella tua prima affermazione str.nil?è vera (come dici tu) il che significa str.empty?che in realtà non verrà chiamato affatto (è così che funziona l' ||operatore). Il resto della tua affermazione è corretta.
Ollie Bennett,

1
Oh buona cattura. Non sono sicuro di come mi sia perso. Correggerò il mio post. Grazie!
Thomas Deranek,

2
è vero che è molto più facile usare la logica sbagliata, ma qui stai controllando cose diverse. la seconda riga equivale a if str && str.empty? non zero || vuoto. l'operatore di navigazione sicura è ancora perfettamente valido da utilizzare per il controllo del flusso puts "hello" unless str&.present?(binari solo per il metodo attuale?)
Sampson Crowley,

1

utilizzato per il controllo zero, come ad esempio in kotlin e swift; con Object -> Swift e Kotlin

model = car?.model

questo modello può essere nullo (Swift) o null (Kotlin) se non abbiamo definito il valore del modello nella classe di auto. usiamo quella e commerciale invece del punto interrogativo in rubino

model = car&.model

se usi car.model senza e commerciale e se il modello è zero il sistema non può continuare a funzionare.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.