Язык программирования Crystal

if var

Если переменная является условием 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

Приёмы описания переменных объекта выше можно применить также и для процедур с методами.