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