QA@IT

Rubyで !! という二重否定を単項演算子として使う理由は?

5679 PV

ときどき見かけて不思議に思っているのですが、

def foo?
  !!bar
end

というイディオムは、どういう意味でしょうか?

def foo?
  bar
end

と書くのと結果は違わないような気がするのですが、そうではないのでしょうか? booleanを返すようにしてるのかなと想像しているのですが、Rubyだとfalseとnil以外はtrueとなるので両者は違わないのでは?

回答

!!bar の意図は bar の値を true か false のどちらかに変換することでしょうが、Ruby 1.9 以降では、! は !@ メソッドの呼び出しということになったので、!! はその意図を完全に実装しているわけではありません。!@ が再定義されていると true でも false でもない値になることがありえます。
それを避ける方法は bar ? true : false というようなものでしょう。

class C
  def !@
    C.new
  end
end
bar = C.new  
p !!bar #=> #<C:0x007fcfa3d6fa90>
p bar ? true : false #=> true

とか。

編集 履歴 (1)
  • ありがとうございます。!@を定義する場面というのがイマイチ想像できないでいますが、、、。bar ? true : false は明示的でいいですね。現実的には !!bar を使うのでしょうけど。 -

trueかfalseを返すようにしておけば、意図せずメソッドの戻り値であるオブジェクトを変更されることがなくなりますので、より堅牢なプログラムになります。

編集 履歴 (0)
  • なるほど! naruseさんの回答と同じかと思いますが、ナットクです。 -

たしかに、Ruby では false と nil 以外は true なのですが、
? メソッドで boolean 以外のオブジェクトが返るのも違和感があるため、
メソッドのインターフェースとして明示的に boolean を返したいという意味合いでこのイデオムを使います。

編集 履歴 (0)
  • ありがとうございます。確かにモヤモヤ感がなくなってみれば、明示的だし、!! があったほうがいいような気がしてきました。 -

true または false 以外を返させないようにするためです。
戻り値を条件式などで使っている分には仰るとおり違わないのですが、
オブジェクトそのものを返すと、それを条件式以外の場所で直接使う子がいたりして、
将来思わぬ非互換をもたらしたりするので。

編集 履歴 (0)
  • 必要以上のものを返さないというのも大事なことなんですね、なるほど。 -

戻り値で nil と false を区別したいときがあるかもしれません。

!!nil    # => false
編集 履歴 (0)
  • ん? しかし、戻り値が true/false になってしまうので、nil が分からなくなるのでは? あれ? -
ウォッチ

この質問への回答やコメントをメールでお知らせします。