Ruby で 1 == true を行うと何が起きるのか
元ネタ
Ruby の true と false がわかってない, いつも if 1 == true みたいなの書いて false 返されてあれ?ってなってる
— ima1zumi (@ima1zumi) June 29, 2020
まあ 1 と true は別オブジェクトだからなーと思いつつ 1 == true は例外を返してほしいなーとか 1 == true は true の方がいいんじゃね?と思ったりしたのでそもそも 1 == がどういう挙動なのかみてみました。
1 == obj を行うと obj.== が呼ばれる
1 == obj みたいな比較を行うと obj が数値でない場合は obj.== を呼び出し、それで比較を行います。
class X def ==(other) true end end x = X.new # 内部で x == 1 が呼び出される p 1 == x # => true
またこの時に注意するのは 1 == x と x == 1 が等価ではないことです。
具体的にいうと戻り値が異なります。
class X def ==(other) "X#==" end end x = X.new p 1 == x # => true p x == 1 # => "X#=="
1 == true するとどうなるのか
では 1 == true を行うとどうなるのかというと内部で true == 1 が呼ばれます。
なので本来は TrueClass#== が呼ばれるはずなんですが TrueClass#== は定義されておらず、実際には親クラスの BasicObject#== が呼び出され結果的に object_id で比較を行います。
むずかしい。
また、次のように TrueClass#== を書き換えると結果を書き換える事ができます。
class TrueClass def ==(other) !!other end end p 1 == true # => true
まあこれはこれで?
まとめ
1 == objするとobj.==が呼ばれる- なので
1 == objがどうなるのかはobj.==の実装に依存する TrueClass/FalseClassでは#==が定義されておらずobject_idで比較が行われる
余談
個人的に 1 == "hoge" みたいなのは例外になって欲しいなーと思っていたんですが、この場合は "hoge".== が呼ばれ String#== の実装に依存します。
で、 String#== の実装は比較できない場合は例外が発生するのではなくて false を返すようになっています。
流石にこれの挙動を変えるのは互換性の面から見てむずかしそうな気がする…。
ちなみに < などで比較すると例外が発生します。
# error: comparison of Integer with true failed (ArgumentError) 1 < true 1 <= false 1 > "hoge"