2
0

More than 1 year has passed since last update.

Rubyで本気で考えるnil safe

Posted at

※ 本記事の内容は作者のブログにも書いているのでこちらも応援お願いします🙇‍♂️

はじめに

以下のようなclassを考えます

class Person
   attr_accessor :first_name, :last_name
   def full_name
     last_name + first_name
   end 
 end

このクラスは、苗字と名前を与えると、full_name関数でそれを合わせた形で取り出すことができます

person = Person.new
person.last_name = '山田'
person.first_name = '太郎'
puts person.full_name # 山田太郎

では、以下の場合はどうでしょうか

person = Person.new
puts person.full_name

実行すると以下のようなエラーが出てしまいました

`full_name': undefined method `+' for nil:NilClass (NoMethodError)
		last_name + first_name

nil safeについて

一般的にプログラミング言語には、何も入っていないことを表すnull (Rubyの場合はnil)という概念があります

変数の中にnullが入ることを考慮せずに処理を書いた場合、意図しないエラーを起こすことがあります (一般的にヌルポと言われるものもこの一部です)
上の例の場合、last_name (およびfirst_name) にnilが入ってしまっているため、エラーが出てしまっているわけです

逆にnullが入ることを考慮し、nullが入っても動く仕組みにすることを null safe (Rubyの場合はnil safe)と言います

rubyにおけるnil safe4パターン

では、上記でエラーを回避するための仕組みを考えてみましょう

[パターン1] begin rescue

エラーが出る前提の元、エラーが出た場合の処理も記述する、と言う方法です

def full_name
  begin
    last_name + first_name
  rescue
    ''
  end
end 

エラーは出ない反面、begin rescue間のエラーを握り潰してしまう(どんなエラーが出たかを考慮しない)という意味ではかなり雑な対処法と言えます。

[パターン2] nil判定

1つ1つの変数でnilかどうかを判定してあげるパターンです

def full_name
  (last_name || '') + (first_name || '')
end 

これはシンプルにかけて良さそうですね

ただし、rubyはfalseとnil以外を全て真値として判断してしまうため(本筋とは外れますが)こんな意地悪なことをされてしまうとエラーになります

person = Person.new
person.last_name = true
person.first_name = '太郎'
puts person.full_name

[実行結果]

`full_name': undefined method `+' for true:TrueClass (NoMethodError)
		(last_name || '') + (first_name || '')
		                  ^

動的型付言語の宿命ではありますが、この場合は(想像している以外の値を入れられているため)エラーを出すのが正しいのかもしれません

[パターン3] to_s method

rubyにおいてnilはclassです

その特性を上手く活かすとこういう回避の仕方もできます

def full_name
  last_name.to_s + first_name.to_s #nil.to_sは空文字を返す
end 

これはnil classが to_s(文字列変換)のメソッドを持っているためです

ちなみに先ほどの意地悪な例はこのパターンだとエラーを出さずに通りますが、これは良い場合と悪い場合がありそうです

person = Person.new
person.last_name = true
person.first_name = '太郎'
puts person.full_name # true太郎

[パターン4] safe navigation operator

いわゆる「 ぽっち演算子 」 と呼ばれる方法です

def full_name
  last_name&.+ first_name
end 

この&.は 左側にある変数がnilの場合には処理を実行せずにnilを返す というものです

(したがってfull_nameはlast_nameがnilの場合はnilを返します)

ただし、逆に言えば以下のような場合は結局再度nil safeを考慮しないといけなくなるため、問題を先送りにしているだけとも言えます

person = Person.new
puts person.full_name + 'さん' # full_nameがnilを返すためここでエラーになる

まとめ

  • rubyのnil safeには様々な方法がある
  • どれも一長一短なので、場合によって使い分けることが重要

上手く使い分けて安全性の高いプログラムを作れるようになりたいものです

2
0
0

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
2
0