Se uma variável for a condição de um if
, dentro do bloco then
a variável será considerada como não tendo o tipo Nil
:
a = alguma_condicao ? nil : 3
# a é Int32 ou Nil
if a
# Já que o único jeito de cair aqui é se "a" for verdadeiro,
# "a" não pode ser nil. Então aqui "a" é Int32.
a.abs
end
Isso também se aplica quando uma variável é atribuída em uma condição if
:
if a = alguma_expressao
# aqui "a" não é nil
end
Essa lógica também se aplica se houver ands (&&
) na condição:
if a && b
# garante-se que aqui tanto "a" quanto "b" não são Nil
end
Aqui, no lado direito da expressão &&
, também é garantido que a
não tem o valor Nil
.
É claro, se o for atribuído novamente o valor da variável dentro do bloco then
, essa variável terá um novo tipo baseado na expressão atribuída.
A lógica acima não funciona com variáveis de instância, variáveis de classe ou variáveis globais:
if @a
# aqui @a pode ser nil
end
Isso acontece porque qualquer chamada de método pode potencialmente afetar essa variável de instância, tornando ela nil
. Outro motivo é que outra thread poderia mudar essa variável de instância após checá-la na condição.
Para fazer algo com @a
somente se ele não for nil
, você tem duas opções:
# Primeira opção: atribua ele a uma variável
if a = @a
# aqui "a" não pode ser nil
end
# Segunda opção: use `Object#try`, encontrado na biblioteca padrão
@a.try do |a|
# aqui "a" não pode ser nil
end
Essa lógica também não funciona com procs e chamadas de métodos, inclusive getters e propriedades, porque não se pode garantir que procs e métodos que podem ser nil
(ou, mais genericamente, procs e métodos do tipo união) retornem o mesmo tipo específico em duas chamadas sucessivas.
if method # primeira chamada ao método que pode retornar Int32 ou Nil
# aqui sabemos que a primeira chamada não retornou Nil
method # a segunda chamada também ainda pode retornar Int32 ou Nil
end
A técnica descrita acima para variáveis de instância também funcionará com procs e chamadas de métodos.