Na maior parte das linguagens true, false são opostos, e nil é como se fosse o oposto a tudo.
Mas isso não é absolutamente verdade em ruby.
1.kind_of?(Integer)
# => nil
!1
# => false
!nil
# => true
A razão para esse comportamento é otimização, por exemplo, se você quer apenas executar um código no caso de determinado método alterar um objeto, você poderia utilizar uma condição como esta:
string = "..."
if string.gsub!('a', 'b')
#string was modified
end
Em casos como este, você não consegue utilizar métodos em cadeia (method chaining) com segurança, afinal, no caso da condição não ser verdadeira, ele retornará nil e a exceção NoMethodError será disparada
string = "cde"
string.gsub!('a','b').downcase!
# => NoMethodError (undefined method `downcase!' for nil:NilClass)
Memoização
Ao usar o operador ||= como operador de memoização deve ser ficar atento a expressões que retornam nil ou false
@cached_value ||= some_expression
# or
cache[:key] ||= some_expression
Exemplos como o acima servem para a maioria dos objetos em Ruby, nesse caso, o valor padrão de @cached_value é nil e enquanto some_expression retorna um valor que não seja nil ou false, isso será chamado apenas uma vez.
No entanto, no caso de some_expression retorna nil ou false ele continuará a ser chamado até que o retorno da expressão não seja nil ou false, e este é um comportamento indesejado quando estamos trabalhando com objetos memoizados.
Por isso quando você quer armazenar em cache uma expressão que possa retornar nil ou false você deve utilizar uma abordagem diferente.
No caso de uma unica variável de instância para o valor 'cacheado', pode-se simplesmente utilizar o método defined? para checar se ela existe, apesar disso resultar em um código mais verboso:
if defined?(@cached_value)
@cached_value
else
@cached_value = some_expression
end
Se você está utilizando hash para armazenar múltiplos valores, pode-se utilizar o método fetch junto de um bloco no caso do valor não for encontrado:
cache.fetch(:key){cache[:key] = some_expression }
Outra vantagem em se utilizar true, nil ou false é que eles são tipos de objetos imediatos, ou seja, não requerem alocação de memoria para serem criados e não criam indireção de memória para serem acessados (memory indirection é quando uma alocação de memória aponta para o endereço de outra alocação e assim por diante)
Top comments (0)