プログラミング言語 Crystal

Crystal の日本語ドキュメントはしばらく更新されておらず情報が古くなっているため、できるだけ原文の英語ドキュメントで最新の情報を参照ください。

翻訳にご協力いただける方は翻訳プロジェクトの issue や Crystal-JP の Slack からお知らせください。

if 変数

もしある変数が if の条件になっているとき、then の分岐内においては、その値が Nil 型ではないと判断されます。

a = some_condition ? nil : 3
# a は Int32 か Nil のいずれか

if a
  # ここに到達するためには if が真でなければならない
  # ということは、a  が nil というのはあり得ないので、ここでは必ず Int32 である
  a.abs
end

このことは、if の条件で変数への代入が行われた場合にも当てはまります。

if a = some_expression
  # ここでは a は nil ではない
end

条件で「かつ (&&)」が使われた場合にも同様です。

if a && b
  # ここでは a も b も Nil でないことが保証される
end

&& 式の右辺が評価された場合、aNil でないことも同時に保証されることがわかると思います。

もちろん、then の分岐内で変数へ再代入を行った場合は、その代入された式に応じて変数の型は変わります。

ただ、インスタンス変数、クラス変数、そしてグローバル変数の場合には、上記が 当てはまらない ことに注意してください。

if @a
  # @a は nil ということもあり得る
end

これは、インスタンス変数はどんなメソッドの実行によっても影響を受ける可能性があるため、それが nil になる場合もあるからです。また、もう1つの理由としては、条件式のチェックが行われた後で、他のスレッドがインスタンス変数を書き換える可能性もあるからです。

@anil ではない場合のみにある処理を実行したい場合、方法は2つあります。

# 方法1: 変数に代入する
if a = @a
  # ここでは a が nil ということはあり得ない
end

# 方法2: 標準ライブラリの `Object#try` を使う
@a.try do |a|
  # ここでは a が nil ということはあり得ない
end

Proc やメソッドの呼び出し (ゲッターやプロパティも含む) の場合にも当てはまりません。なぜなら、Nil を許容する (もしくは、複数の型の組み合わせとなるユニオン型の場合がより一般的でしょう) Proc やメソッドの呼び出しの場合、連続した呼び出しであっても、それらが常に同じ型を返すとは限らないからです。

if method # メソッドの最初の呼び出し (メソッドは Int32 か Nil を返すとする)
          # ここで、最初の呼び出しが Nil を返していないことはわかっている
  method  # しかし、2回目の呼び出しは、また Int32 か Nil のどちらかを返す
end

こういった Proc やメソッド呼び出しの場合にも、上記でインスタンス変数に関して記載したテクニックが有効です。