if var.nil?

if var.nil?

값이 nil인지 아닌지에 따라 분기해야 할 때가 많죠. if var.nil?는 그런 상황에서 컴파일러가 변수의 타입을 정확히 좁혀주는 기능이에요.

출처: Crystal 공식 문서

본문

if의 조건이 var.nil?이면, 컴파일러는 then 분기에서 var의 타입을 Nil로, else 분기에서 non-Nil로 알 수 있어요.

a = some_condition ? nil : 3
if a.nil?
  # here a is Nil
else
  # here a is Int32
end

인스턴스 변수

if var.nil?를 통한 타입 제한은 지역 변수(local variable)에만 적용돼요. 위 예시와 비슷한 코드에서 인스턴스 변수의 타입은 여전히 nilable이고, greetunless 분기에서 String을 기대하므로 컴파일 오류가 나요.

class Person
  property name : String?

  def greet
    unless @name.nil?
      puts "Hello, #{@name.upcase}" # Error: undefined method 'upcase' for Nil (compile-time type is (String | Nil))
    else
      puts "Hello"
    end
  end
end

Person.new.greet

이 문제는 값을 먼저 지역 변수에 저장하면 해결할 수 있어요.

def greet
  name = @name
  unless name.nil?
    puts "Hello, #{name.upcase}" # name will be String - no compile error
  else
    puts "Hello"
  end
end

이것은 Crystal의 멀티스레딩의 부산물이에요. Fiber가 존재하기 때문에, Crystal은 컴파일 시점에 if 분기에서 사용할 때 인스턴스 변수가 여전히 non-Nil인지 알 수 없어요.

더 알아보기

  • if var.nil? 타입 제한은 지역 변수에만 적용된다는 점을 기억하세요. 인스턴스 변수는 먼저 지역 변수에 담아서 쓰는 게 일반적인 패턴이에요.
  • 관련 패턴으로 if var 문서도 참고할 수 있어요.