Que hace &. (punto y signo) significa en Ruby?

Respuestas:

205

Se llama el operador de navegación segura. Introducido en Ruby 2.3.0, le permite invocar métodos en objetos sin preocuparse de que el objeto pueda ser nil(evitando un undefined method for nil:NilClasserror), similar al trymétodo en Rails .

Entonces puedes escribir

@person&.spouse&.name

en vez de

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

De los documentos :

my_object.my_method

Esto envía el mensaje my_method a my_object. Cualquier objeto puede ser un receptor pero, dependiendo de la visibilidad del método, enviar un mensaje puede generar un NoMethodError.

Puedes utilizar &. para designar un receptor, entonces my_method no se invoca y el resultado es nulo cuando el receptor es nulo. En ese caso, los argumentos de my_method no se evalúan.

Santhosh
fuente
13
en realidad, funciona como el try!método, notry
Grzegorz
58

Nota: Aunque @Santosh dio una respuesta clara y completa, me gustaría agregar más antecedentes y una nota importante sobre su uso con variables que no son de instancia.


Se llama " Operador de navegación segura " ( también conocido como "Operador de encadenamiento opcional", "Operador condicional nulo", etc. ). Matz parece llamarlo "operador solitario". Fue introducido en Ruby 2.3 . Envía un método a un objeto solo si no lo es nil.

Ejemplo:

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

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

"Caso de borde" con variables locales:

Tenga en cuenta que el código anterior usa variables de instancia. Si desea utilizar un operador de navegación seguro con variables locales, deberá verificar que sus variables locales se definan primero.

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

Para solucionar este problema, verifique si su variable local se define primero o configúrela en 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

Fondo del método

Rails tiene un trymétodo que básicamente hace lo mismo. Utiliza el sendmétodo internamente para llamar a un método. Matz sugirió que es lento y que esta debería ser una función de lenguaje incorporada.

Muchos otros lenguajes de programación tienen características similares: Objetivo C, Swift, Python, Scala, CoffeeScript, etc. Sin embargo, una sintaxis común es ?.(punto de pregunta). Pero, esta sintaxis no pudo ser adoptada por Ruby. Debido a que ?estaba permitido en los nombres de métodos y, por lo tanto, la ?.secuencia de símbolos ya es un código Ruby válido. Por ejemplo:

2.even?.class  # => TrueClass

Es por eso que la comunidad Ruby tuvo que proponer una sintaxis diferente. Fue una discusión activa y se consideraron diferentes opciones ( .?, ?, &&, etc.). Aquí hay una lista de algunas consideraciones:

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

# And finally
u&.profile&.thumbnails

Al elegir la sintaxis, los desarrolladores observaron diferentes casos límite y la discusión es bastante útil. Si desea ver todas las variantes y matices del operador, consulte esta discusión de introducción de características en el rastreador oficial de problemas de Ruby.

Uzbekjon
fuente
Que es '.?' ? Creo que se decidió que esta sintaxis no sería posible de usar. Solo puedo obtener '&.' trabajar.
Keith Bennett
2
Exactamente, como escribí .?y se consideraron otras opciones, ¡pero &.se eligió! Entonces, solo &.funcionará.
Uzbekjon
Oh, lo siento, no leí completamente hasta el final. ¿No sería mejor si los ejemplos tuvieran la sintaxis correcta?
Keith Bennett
@KeithBennett Entiendo a qué te refieres. Tuve un error tipográfico en mi primer ejemplo. Tienes razón, debe ser &.y no .?, mi mal. Actualicé mi respuesta. ¡Gracias por el aviso!
Uzbekjon el
@Uzbekjon Todavía hay un error tipográfico en el ejemplo de caso de Edge (opción 1): user.?profiledebería ser user&.profile(¡no podría editarlo yo mismo, ya que es solo una edición de 1 carácter!).
Yanis Vieilly
7

¡Sé cauteloso! Aunque el operador de navegación segura es conveniente, también puede ser fácil engañarse para cambiar su lógica. Recomiendo evitar su uso en el control de flujo. Ejemplo:

str = nil

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

En el primer ejemplo, str.nil?devuelve truey str.empty?nunca se llama, lo putsque hace que se ejecute la instrucción. Sin embargo, en el segundo ejemplo, str&.empty?devuelve el nilcual es falsey, y la putsdeclaración nunca se ejecuta.

Thomas Deranek
fuente
Interesante, aunque técnicamente en su primer enunciado str.nil?es verdadero (como usted dice), lo que significa str.empty?que en realidad no se llamará en absoluto (así es como funciona el ||operador). El resto de su declaración es correcta.
Ollie Bennett
1
Oh buena captura. No estoy seguro de cómo me perdí eso. Corregiré mi publicación. ¡Gracias!
Thomas Deranek
2
Es cierto que es mucho más fácil usar la lógica incorrecta, pero aquí está comprobando diferentes cosas. la segunda línea es equivalente a if str && str.empty? no nulo || vacío. el operador de seguridad de la navegación sigue siendo perfectamente válido para el uso para el control de flujo puts "hello" unless str&.present?(carriles sólo para el presente método?)
Sampson Crowley
1

se usaba para verificación nula, como en kotlin y swift Por ejemplo; con Objeto -> Swift y Kotlin

model = car?.model

este modelo puede ser nulo (Swift) o nulo (Kotlin) si no hemos definido el valor del modelo en la clase de automóvil. usamos ese signo en lugar de signo de interrogación en rubí

model = car&.model

si usa car.model sin ampersand y si el modelo es nulo, el sistema no puede continuar ejecutándose.

Umut ADALI
fuente