LoginSignup
31
22

More than 5 years have passed since last update.

rubyでnilを意識しないスマートなコードを書く

Last updated at Posted at 2016-06-02

nilを意識しないとは

nilに対して不正なメソッド呼び出しをしようとした時には、その結果がnilであることを期待するようプログラミングをするケースが多いと思います。 そして、その場合には下記のようにActiveSupportのtry!メソッドを使用することが多いと思います。

result = nil
result.try!(&:first_method).try!(&:second_method) # => nil

上記のように、nilのときにはメソッド呼び出しを行わない、ということを分岐を書かずに実現することをnilを意識しないと言っています。

※追記:Ruby2.3以降なら通称ぼっち演算子がつかえるそうなので、そちらがベターだと思う。

result = nil
result&.first_method&.second_method # => nil

try!やifを重ねるのは美しくない

result = nil
return result.try!(&:first_method).try!(&:second_method) 
result = nil
return nil if result.nil?
temp = result.first_method
return temp.nil? ? nil : temp.second_method 

nilの可能性がある変数に対し、nilの場合はメソッド呼び出しを行わない場合、毎回try!やif分岐を加えると、なんだか読みづらくなります。
if分岐に至っては、メソッドチェーンができなくなり最悪です。

NilClass.method_missingのオーバーライドという黒魔術

なぜnilの場合にメソッドを呼び出さないようにするかというと、nilではない他のクラスのインスタンスに実装されているメソッドを呼びだそうとしている時にnilがレシーバとなると、呼び出すはずのメソッドが存在せず、NilClassからNoMethodErrorが発生するからです。
そして、NoMethodErrorは、存在しないメソッドが呼び出された時に実行されるmethod_missingメソッド内でraiseされています。

つまり、NilClassのmethod_missingメソッドをオーバーライドし、superを呼びださなければNoMethodErrorは発生しないことになります。NoMethodErrorが発生しなければ、try!やif分岐を組む必要もありません。
さらに、nilを返すことでtry!と同じような挙動を実現できます。

class NilClass
  def method_missing(name, *args, &block)
    nil
  end
end

result = nil
result.first_method.second_method # => nil

使う時は慎重に

try!を使わなくてもnilをスルーできるのでコード量が減り、スマートに書くことが出来るようになりますが、タイポに気づきにくくなる等、弊害もあります。

result = nil
result.to_S # => nil
result.try!(&:to_S) # => NoMethodError: undefined method `to_S' for nil:NilClass

try!の場合はレシーバがnilの時にメソッドを呼ばない。
method_missingのオーバーライドの場合は、NilClassに存在しないメソッドが呼ばれてもエラーが出ないようにする。

というアプローチの違いから起きる弊害です。

31
22
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
22