Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Ruby: RuboCopで== trueや== falseが怒られない理由

更新情報

  • 2019/02/15: 初版公開
  • 2022/10/20: 細部を更新

rubocop/rubocop - GitHub

前置き: nil == falseの結果はfalse

Rubyの本には「条件分岐でnilfalse以外のすべてのオブジェクトがtrueとみなされます」と書かれているのが定番です。

しかし以下のとおり、nilfalse別のオブジェクトです。

nil == false #=> false

nil?も同じことをおっしゃっています。

nil.nil?    #=> true
false.nil?  #=> false
true.nil?   #=> false

nilfalseが同一視されるのは、あくまで条件分岐ifcase)のときです。

a = nil
if a
  "true!"
else
  "false!"
end
#=> "false!"

上のようにanilからfalseに変えても結果は変わりません。

Rubyにtrue?メソッドやfalse?メソッドはない

ちなみにtrue?false?といった冗長かつ悪手にしかならないメソッドはデフォルトのRubyにはありません。

true.true?   #=> NoMethodError: undefined method `true?' for true:TrueClass
false.false? #=> NoMethodError: undefined method `false?' for false:FalseClass

if a == trueのようにわざわざ== trueだの== falseだのtrue?だのfalse?だのをモロ出しに書くことを避けるのがRuby流です。

Ruby作者のお気持ちを、これまで見かけた発言などから想像してみました。仮にtrue?false?が「あり」だとすると、それを当てにしてたとえば以下のような無節操かつ欲張りさんなメソッドを書きまくって二値論理をぶち壊しにする人が続出するかもしれないということなのだと思います(外してたらすみません)。

  • truefalsenilを返し、しかもtruefalsenilの意味が異なる😨
  • truefalsenilの他にも値を返し、しかもそれぞれ全部意味が異なる😰

まあnil?があるからfalsenilの区別はやろうと思えばやれてしまうし、そうするしかない感じのメソッドもあったりしますが、あまりよろしくなさそうですね。

Ruby作者は少なくとも「Booleanクラスは作らない」と言ってたと思います(理由はちょっと違いますが↓)。

参考: !!はRuboCopに怒られる

一部地域で!!を使ってtrue/falseに揃える人もいると聞きましたが、デフォルトではRuboCopに怒られます

# frozen_string_literal: true

a = false
'false' if !!a
# 上にRuboCopをかける
#=> test.rb:4:12: C: Style/DoubleNegation: Avoid the use of double negation (!!).
'false' if !!a

参考: Style/DoubleNegation -- Style :: RuboCop Docs

RUboCopのStyle/DoubleNegationでは「!!something!something.nil?の結果は同じではない」と説明しています。ごもっともです。

Rubyは「ナマのtrue/false」で考えずに済むように書こう

いつもお世話になってるkazzさんが「Rubyプログラマーは明示的なtrue/falseを意識するべきではないヨ」と指摘してくれました。

言い換えれば、Rubyでは条件分岐をナマのtrue/falseで考えるのではなく、条件そのもので考えようよ、ということですね😋。上で!!がRuboCopに怒られるのも、わざわざtrue/falseに還元するでないということでしょうね。

ビジネスロジックをいちいち原始的なtrue/falseに変換するのではなく、たとえば#paid?のように条件そのものをメソッド化する方が可読性も上がります。

if goods.paid == true  # 残念な書き方
...


if goods.paid?         # Rubyらしい書き方
...

本題: Rubocopで== true== falseが怒られない理由

しかし、単純に== true== falseを書く人をひっ捕らえてお尻をぺんぺんすればいいというものではなさそうです。

== true== falseという残念なはずの書き方は、意外にもRuboCopに怒られません。以下は素のRuboCopでエラーなしとなります。

# frozen_string_literal: true

a = false
'false' if a == false

追記(2022/10/20)

上の結果はRuby 3.1.2とRuboCop 1.36.0でも変わっていません。

Rubyスタイルガイド「2-61【統一】比較条件ではeven?やzero?などの述語メソッドが望ましい(==の直接使用は避ける」では、== nilといった書き方は望ましくないという記述はありますが、== true== falseが望ましくないとは記載されていません。

追記(2022/10/20)

上の記述は最新のRubyスタイルガイドでも変わっていません。

参考: Predicate Methods -- Ruby Style Guide

推測ですが、RuboCop奉行の立場としては以下のような理由があるのではないでしょうか。

  • nil?と違って、true?false?がRubyにないので、それらを使えと単純にテンプレメッセージで警告しにくい
  • 外部API様のご都合などで== true== falseと書くしかない状況がそこそこある

仮にお奉行様が== true== falseを無慈悲にビシビシ取り締まったら、それを回避するためにラッパーを書くなどの手間の方が大きくなり、あちこちで一揆の火の手があがるかもしれません。

落とし所としては、== true== falseを絶対悪として根絶するほどではないにしても、この書き方をするしかなかったときに「世間に負けた」と軽く敗北感を覚える方がよさそうです。「書いたらダメ」より「書いたら負け」の精神で。

おまけ: Pythonの場合

ついでにPythonのスタイルガイドを覗いてみると、== True== Falseと書いてはいけないとありました。これも二値論理を壊さないためと思われますが、言語ではなくスタイルガイドでの制約ですね。

Yes: if greeting:
No: if greeting == True:
Worse: if greeting is True:
同スタイルガイドより

なお、以下のようにPythonでは以下がすべてfalseになります。このあたりはRubyとはお家の事情が異なるということで。

  • 偽であると定義されている定数: NoneFalse
  • 数値型におけるゼロ: 0, 0.0, 0j, Decimal(0), Fraction(0, 1)
  • 空のシーケンスまたはコレクション: '', (), [], {}, set(), range(0)

参考: Python ドキュメント「偽であると定義されている定数

おたより発掘

関連記事

Rubyにおけるunlessとコードの読みやすさについて

【保存版】Rubyスタイルガイド(日本語・解説付き)総もくじ


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。