コンテンツにスキップ

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

ここでは、`&&`式の右辺も`a`が`Nil`ではないことが保証されます。

もちろん、`then`ブランチ内で変数を再代入すると、その変数は代入された式に基づいて新しい型を持ちます。

制限事項

上記のロジックは**ローカル変数に対してのみ**機能します。インスタンス変数、クラス変数、クロージャにバインドされた変数では機能しません。これらの種類の変数の値は、条件がチェックされた後に別のファイバーによって影響を受ける可能性があり、`nil`になる可能性があります。また、定数でも機能しません。

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

メソッド呼び出し

このロジックは、ゲッターやプロパティを含むprocやメソッドの呼び出しでも機能しません。なぜなら、`nil`を許容する(より一般的には共用型)procやメソッドは、2回連続で呼び出されたときに同じ、より具体的な型を返すことが保証されないからです。

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とメソッドの呼び出しにも有効です。