Если переменная является условием if
, то внутри ветки then
она будет рассматриваться как переменная типа, отличного от Nil
:
a = some_condition ? nil : 3
# a может быть Int32 или Nil
if a
# Поскольку попасть сюда возможно только если a истинно (truthy),
# то a не может быть nil. А значит она Int32.
a.abs
end
То же происходит, когда переменная a задаётся в условии if
:
if a = some_expression
# здесь a не nil
end
Эта же логика действует, если в условии если логическое И (&&
):
if a && b
# здесь обе переменные a и b точно не будут Nil
end
Здесь правая часть выражения &&
также гарантирует, что a
не будет Nil
.
Само собой, в случае переопределения переменной внутри ветки then
тип переменной будет изменён в зависимости от переопределяющего выражения.
Приведённая выше логика не действует с переменными объекта, класса и глобальными переменными:
if @a
# здесь @a может быть nil
end
Так происходит потому, что любой вызов метода в теории может привести переменную объекта к nil
. Другая причина заключается в том, что другой поток может изменить значение этой переменной объекта после проверки условия.
Есть два варианта сделать что-нибудь с @a
если она не nil
:
# Первый вариант: назначить переменную a
if a = @a
# здесь a не будет nil
end
# Второй вариант: использовать `Object#try` из стандартной библиотеки
@a.try do |a|
# здесь a не будет nil
end
Эта логика также не работает с вызовами процедур и методов, включающих геттеры и свойства, поскольку процедуры и методы, которые могут быть nil (или, если брать шире, относятся к объединённому типу), не могут гарантировать возвращение одного и того же типа при двух успешных вызовах.
if method # первый вызов метода может вернуть Int32 или Nil
# здесь мы узнали, что первый вызов вернул не Nil
method # второй вызов всё ещё может вернуть Int32 или Nil
end
Приёмы описания переменных объекта выше можно применить также и для процедур с методами.