O que &. (ponto comercial) em Ruby?

Respostas:

205

É chamado de Operador de Navegação Segura. Introduzido no Ruby 2.3.0, permite chamar métodos em objetos sem se preocupar que o objeto possa ser nil(Evitando um undefined method for nil:NilClasserro), semelhante ao trymétodo no Rails .

Então você pode escrever

@person&.spouse&.name

ao invés de

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

Dos documentos :

my_object.my_method

Isso envia a mensagem my_method para my_object. Qualquer objeto pode ser um receptor, mas, dependendo da visibilidade do método, o envio de uma mensagem pode gerar um NoMethodError.

Você pode usar &. para designar um receptor, então my_method não é chamado e o resultado é nulo quando o receptor é nulo. Nesse caso, os argumentos de my_method não são avaliados.

Santhosh
fonte
13
na verdade, ele funciona como o try!método, nãotry
Grzegorz
58

Nota: Embora o @Santosh tenha dado uma resposta clara e completa, gostaria de adicionar um pouco mais de background e uma nota importante sobre seu uso com variáveis ​​que não são de instância.


É chamado de " Operador de Navegação Segura " ( também conhecido como "Operador de encadeamento opcional", "Operador nulo-condicional" etc. ). Matz parece chamá-lo de "operador solitário". Foi introduzido no Ruby 2.3 . Ele envia um método para um objeto apenas se não estiver nil.

Exemplo:

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

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

"Edge case" com variáveis ​​locais:

Observe que o código acima usa variáveis ​​de instância. Se você deseja usar um operador de navegação seguro com variáveis ​​locais, deverá verificar se suas variáveis ​​locais estão definidas primeiro.

# `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 corrigir esse problema, verifique se sua variável local está definida primeiro ou defina-a como nulo:

# 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

Antecedentes do método

O Rails possui um trymétodo que basicamente faz o mesmo. Ele usa o sendmétodo internamente para chamar um método. Matz sugeriu que é lento e esse deve ser um recurso de idioma interno.

Muitas outras linguagens de programação têm características semelhantes: Objective C, Swift, Python, Scala, CoffeeScript, etc. No entanto, uma sintaxe comum é ?.(ponto de interrogação). Mas, essa sintaxe não pôde ser adotada pelo Ruby. Como ?foi permitido nos nomes dos métodos e, portanto, a ?.sequência de símbolos já é um código Ruby válido. Por exemplo:

2.even?.class  # => TrueClass

É por isso que a comunidade Ruby teve que criar uma sintaxe diferente. Foi uma discussão activa e diferentes opções foram consideradas ( .?, ?, &&, etc.). Aqui está uma lista de algumas considerações:

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

# And finally
u&.profile&.thumbnails

Ao escolher a sintaxe, os desenvolvedores analisaram diferentes casos extremos e a discussão é bastante útil. Se você deseja passar por todas as variantes e nuances do operador, consulte a discussão sobre introdução de recursos no rastreador oficial de problemas do Ruby.

Uzbekjon
fonte
O que é '.?' ? Eu acho que foi decidido que essa sintaxe não seria possível de usar. Só consigo 'e'. trabalhar.
21416 Keith Bennett
2
Exatamente, como escrevi .?e outras opções foram consideradas, mas &.foram escolhidas! Então, só &.vai funcionar.
Uzbekjon
Oh, desculpe, eu não li completamente até o final. Não seria melhor se os exemplos tivessem a sintaxe correta?
21416 Keith Bennett
@KeithBennett Entendo o que você quer dizer. Eu tive um erro de digitação no meu primeiro exemplo. Você está certo, deve ser &.e não.? , meu mal. Atualizei minha resposta. Obrigado pela atenção!
Uzbekjon
@Uzbekjon Ainda existe um erro de digitação no exemplo do caso Edge (opção 1): user.?profiledeveria ser user&.profile(não poderia editá-lo eu mesmo, pois é apenas uma edição de 1 caractere!).
Yanis Vieilly
7

Seja cauteloso! Embora o operador de navegação seguro seja conveniente, também pode ser fácil enganar-se a mudar sua lógica com ele. Eu recomendo evitar o uso dele no controle de fluxo. Exemplo:

str = nil

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

No primeiro exemplo, str.nil? retorna truee str.empty?nunca é chamado, fazendo com que a putsinstrução seja executada. No segundo exemplo, no entanto, str&.empty?retorna nilfalse, e a putsinstrução nunca é executada.

Thomas Deranek
fonte
Interessante, embora tecnicamente em sua primeira declaração str.nil?seja verdadeira (como você diz), o que significa str.empty?que na verdade não será chamado (é assim que o ||operador funciona). O restante da sua declaração está correto.
Ollie Bennett
1
Oh boa captura. Não sei bem como senti falta disso. Eu vou corrigir minha postagem. Obrigado!
Thomas Deranek
2
é verdade que é muito mais fácil usar a lógica errada, mas você está verificando coisas diferentes aqui. a segunda linha é equivalente a se str && str.empty? não zero || vazio. o operador de navegação segura ainda é perfeitamente válido para uso para controle de fluxo puts "hello" unless str&.present?(trilhos só para o presente método?)
Sampson Crowley
1

usado para verificação nula, como em kotlin e swift Por exemplo; com Objeto -> Swift e Kotlin

model = car?.model

esse modelo pode ser nulo (Swift) ou nulo (Kotlin) se não tivermos definido o valor do modelo na classe de carro. usamos esse e comercial em vez de ponto de interrogação em rubi

model = car&.model

se usar car.model sem e comercial e se o modelo for nulo, o sistema não poderá continuar em execução.

Umut ADALI
fonte