if var

if var

변수가 if의 조건이면, then 분기 안에서 그 변수는 Nil 타입이 아닌 것으로 취급돼요. 이 논리는 if의 조건에서 변수를 할당할 때나 && 연산자가 있을 때도 적용되지만, 지역 변수에만 동작해요.

출처: Crystal 공식 문서 — if var

본문

변수 하나가 if의 조건이면, then 분기 안에서 그 변수는 Nil 타입이 아닌 것으로 간주돼요:

a = some_condition ? nil : 3
# a is Int32 or Nil

if a
  # Since the only way to get here is if a is truthy,
  # a can't be nil. So here a is Int32.
  a.abs
end

이것은 if의 조건에서 변수를 할당할 때도 적용돼요:

if a = some_expression
  # here a is not nil
end

이 논리는 조건에 && 연산자가 있을 때도 적용돼요:

if a && b
  # here both a and b are guaranteed not to be Nil
end

여기서 && 표현식의 오른쪽에도 aNil이 아님이 보장돼요.

물론 then 분기 안에서 변수를 다시 할당하면, 그 변수는 할당된 표현식에 기반한 새 타입을 갖게 돼요.

한계 (Limitations)

위 논리는 지역 변수에만 동작해요. 인스턴스 변수, 클래스 변수, 클로저 안에 묶인 변수에는 동작하지 않아요. 이런 변수들은 조건을 확인한 뒤 다른 파이버(fiber)에 의해 값이 바뀌어 nil이 될 수 있기 때문이에요. 상수(constant)에서도 동작하지 않아요.

if @a
  # here `@a` can be nil
end

if @@a
  # here `@@a` can be nil
end

a = nil
closure = -> { a = "foo" }

if a
  # here `a` can be nil
end

이것은 값을 새 지역 변수에 할당하면 우회할 수 있어요:

if a = @a
  # here `a` can't be nil
end

또 다른 방법은 표준 라이브러리의 Object#try를 쓰는 거예요. 이 메서드는 값이 nil이 아닐 때만 블록을 실행해요:

@a.try do |a|
  # here `a` can't be nil
end

메서드 호출 (Method calls)

그 논리는 proc과 메서드 호출에도 동작하지 않아요. getter와 프로퍼티를 포함해요. nilable (더 일반적으로는 유니온 타입) proc과 메서드는 두 번 연속 호출했을 때 같은 더 구체적인 타입을 반환한다는 보장이 없기 때문이에요.

if method # first call to a method that can return Int32 or Nil
  # here we know that the first call did not return Nil
  method # second call can still return Int32 or Nil
end

인스턴스 변수에 대해 위에서 설명한 기법들은 proc과 메서드 호출에도 마찬가지로 동작해요.

더 알아보기