読者です 読者をやめる 読者になる 読者になる

Crystal のインスタンス変数が nilable になってつらい

これは「Ruby脳にはCrystalつらい Advent Calendar 2015」の12日目の記事です。

qiita.com

Crystal で Ruby と同じような感覚でインスタンス変数を使うと nil だと怒られることがあります。

class Hoge
  def hoge
    @hoge = "abc"
    @hoge.size
  end
end

Hoge.new.hoge
% crystal hoge.cr
Error in ./hoge.cr:8: instantiating 'Hoge#hoge()'

Hoge.new.hoge
         ^~~~

in ./hoge.cr:4: undefined method 'size' for Nil (compile-time type is String?)

    @hoge.size
          ^~~~

================================================================================

Error: instance variable '@hoge' of Hoge was not initialized in all of the 'initialize' methods, rendering it nilable

Specifically in these ones:

initialize メソッドで初期化されていないインスタンス変数は、暗黙的に nil で初期化されるためです。 つまり上の例は、次と同じです。

class Hoge
  def initialize
    @hoge = nil
  end

  def hoge
    @hoge = "abc"
    @hoge.size
  end
end

@hoge は Nil または String のどちらの値にもなりうるので、Nil にない size メソッドを使うとエラーになります。

エラーにならないようにするには initialize で文字列として初期化してしまえばよいです。

class Hoge
  def initialize
    @hoge = ""
  end

  def hoge
    @hoge = "abc"
    @hoge.size
  end
end

または明に String として宣言することもできます。

class Hoge
  def initialize
    @hoge :: String
  end

  def hoge
    @hoge = "abc"
    @hoge.size
  end
end

追記 2016-05-14

最後の例は 0.12 から使えなくなりました。