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"